For my Django application, I'm looking to keep a full edit history for all objects. As part of this, I've overridden the model's save() method, part of which is shown below:
# Replicate the current version (from the db) with all attributes unchanged
new_ver = self.__class__.objects.get(pk=self.pk).save(force_insert=True)
# Update the current version in the database with the new attributes
super(CodexBaseClass, self).save(*args, force_update=True, **kwargs)
The 'self' that's passed to the save() method is the NEW version of the object that's been generated by the form. What this code is attempting to do is
(1) Make a copy of the object as it currently appears in the database (ie: copy the data as it was before the form modified it), then force an insert of this data so it's copied as a new row
(2) Update the existing row with the new version of the object that's been submitted through the form.
The problem is with the first line of the two lines of code - It generates a DoesNotExist exception. The object does exist, so I'm currently thinking that the issue is that the database row it's trying to read is currently locked.
So my question is: Is there a way I can modify/replace the first line so that I have a copy of the initial data, as it was before the form modified it?
Thanks.
If you want to insert a new object with same attributes, you only need to change the primary key of your object, and save it.
new_ver = self.__class__.objects.get(pk=self.pk)
new_ver.pk = None
new_ver.save()
Using None as primary key will auto generates it. You can have more information if you look at the django documentation.
If you need you can also make a copy of your object, be careful the cost can be expensive :
from copy import deepcopy
ver = self.__class__.objects.get(pk=self.pk)
new_ver = deepcopy(ver)
new_ver.pk = None
new_ver.save()
# Do what you need with ver object
You should take a look at django-reversion.
django-reversion is an extension to the Django web framework that
provides version control for model instances.
Documentation: link
Features
Roll back to any point in a model instance’s history.
Recover deleted model instances.
Simple admin integration.
Related
I am new to Python and was studying FastApi and SQL model.
Reference link: https://sqlmodel.tiangolo.com/tutorial/fastapi/session-with-dependency/#the-with-block
Here, they have something like this
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.from_orm(hero)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
Here I am unable to understand this part
session.add(db_hero)
session.commit()
session.refresh(db_hero)
What is it doing and how is it working?
Couldn't understand this
In fact, you could think that all that block of code inside of the create_hero() function is still inside a with block for the session, because this is more or less what's happening behind the scenes.
But now, the with block is not explicitly in the function, but in the dependency above:
It's an explanation from docs what is a session
In the most general sense, the Session establishes all conversations
with the database and represents a “holding zone” for all the objects
which you’ve loaded or associated with it during its lifespan. It
provides the interface where SELECT and other queries are made that
will return and modify ORM-mapped objects. The ORM objects themselves
are maintained inside the Session, inside a structure called the
identity map - a data structure that maintains unique copies of each
object, where “unique” means “only one object with a particular
primary key”.
So
# This line just simply create a python object
# that sqlalchemy would "understand".
db_hero = Hero.from_orm(hero)
# This line add the object `db_hero` to a “holding zone”
session.add(db_hero)
# This line take all objects from a “holding zone” and put them in a database
# In our case we have only one object in this zone,
# but it is possible to have several
session.commit()
# This line gets created row from the database and put it to the object.
# It means it could have new attributes. For example id,
# that database would set for this new row
session.refresh(db_hero)
I want to serialize an object to Json, then deserialize it as the object, without save it to DB (it's already saved). The reason is that the current state of the model may be different from the state when I serialized it.
This is how I currently serialize the object:
ser_obj = serializers.serialize("json", [self.instance])
Now, as I understand, in order to deserialize I can do something like:
for obj in serializers.deserialize("json", ser_obj):
obj.save()
But that will save the object to DB, which I don't want to.
I guess I can also do something like:
MyModel(field1 = ser_obj['field1'],
field2 = ser_obj['field2']
)
But it seems wrong.
So any idea how can I deserialize json object into Django model without saving to DB?
I did notice that if I use the 'save()', I can then use the obj.object to get the object.
You can deserialise without saving to the database.
for obj in serializers.deserialize("json", ser_obj):
do_something_with(obj)
This will return DeserializedObject instances. You can find more info in the Django docs
I wanted to solve a similar problem and found you can access the objects without saving by accessing the .object property:
for deserializedobject in serializers.deserialize("json", ser_obj):
obj = deserializedobject.object
print(object.field1)
Note that if objects accessed in this way have a related field (eg ForeignKey) then that will point to the current database object with that pk even if one has just been deserialised.
ie if A points to B and both A and B are serialised and later deserialised then A will point to B on the database which may have different values than the deserialised.
Essentially you cannot use this approach to freeze a snapshot of a group of related objects and interrogate them at a later date without executing a save().
(as at Django 3.2.3)
I want to override the save() method of my model and check changes to some of the fields:
def save(self):
if self.counter != self.original_counter: # that's what I want
...
I saw this question was asked before and the answer was to get the object from the db and compare the db value with the current value:
def save(self):
original = MyModel.objects.get(pk=self.pk)
if self.counter != original.counter:
...
but that's a waste of a db query, it's easy to get what I want if on every instance initialization the __init__ method will initialize 2 attributes for each field - obj.<attr> and also obj.original_<attr>, do I need to implement it myself or is there a django package that can do it for me?
I don't think there is a way you can get the original values like that. Even if you implement the pseudo original_* fields yourself, you'd end up doing a MyModel.objects.get(...) anyways.
The issue is that inside the save() method, the object has already been saved. So you see the new values. There is no way to see the original values without querying the database.
I am using Django 1.5.1 and I want to save or update model.
I read the django document and I met the get_or_create method which provides saving or updating. There is a usage like;
Model.objects.get_or_create(name='firstName',surname='lastName',defaults={'birthday': date(1990, 9, 21)})
defaults field is using only for getting. While it is setting phase, name and surname are only set. That is what I understand from the document.
So I want to do something different that setting name,surname and birthDay, but getting name and surname excluding birthdate. I could not see the way to do that in the document and another place.
How can I do this?
Thank you!
get_or_create provides a way of getting or creating. Not saving or updating. Its idea is: I want to get a model, and if it doesn't exist, I want to create it and get it.
In Django, you don't have to worry about getting the name or the surname or any attribute. You get an instance of the model which has all the attributes, I.e.
instance = Model.objects.get(name='firstName',surname='lastName')
print instance.birthday
print instance.name
print instance.surname
An overview of the idea could be: a Model is a data structure with a set of attributes, an instance is a particular instance of a model (uniquely identified by a primary_key (pk), a number) which has a specific set of attributes (e.g. name="firstName").
Model.objects.get is used to go to the database and retrieve a specific instance with a specific attribute or set of attributes.
Since Django 1.7 there's update_or_create:
obj, created = Person.objects.update_or_create(
first_name='John',
last_name='Lennon',
defaults=updated_values
)
The parameters you give are the ones that will be used to find an existing object, the defaults are the parameters that will be updated on that existing or newly created object.
A tuple is returned, obj is the created or updated object and created is a boolean specifying whether a new object was created.
Docs: https://docs.djangoproject.com/en/1.8/ref/models/querysets/#update-or-create
I'm new to Python. I'm trying to figure out how to emulate an existing application I've coded using PHP and MS-SQL, and re-create the basic back-end functionality on the Google Apps Engine.
One of the things I'm trying to do is emulate the current activity on certain tables I have in MS-SQL, which is an Insert/Delete/Update trigger which inserts a copy of the current (pre-change) record into an audit table, and stamps it with a date and time. I'm then able to query this audit table at a later date to examine the history of changes that the record went through.
I've found the following code here on stackoverflow:
class HistoryEventFieldLevel(db.Model):
# parent, you don't have to define this
date = db.DateProperty()
model = db.StringProperty()
property = db.StringProperty() # Name of changed property
action = db.StringProperty( choices=(['insert', 'update', 'delete']) )
old = db.StringProperty() # Old value for field, empty on insert
new = db.StringProperty() # New value for field, empty on delete
However, I'm unsure how this code can be applied to all objects in my new database.
Should I create get() and put() functions for each of my objects, and then in the put() function I create a child object of this class, and set its particular properties?
This is certainly possible, albeit somewhat tricky. Here's a few tips to get you started:
Overriding the class's put() method isn't sufficient, since entities can also be stored by calling db.put(), which won't call any methods on the class being written.
You can get around this by monkeypatching the SDK to call pre/post call hooks, as documented in my blog post here.
Alternately, you can do this at a lower level by implementing RPC hooks, documented in another blog post here.
Storing the audit record as a child entity of the modified entity is a good idea, and means you can do it transactionally, though that would require further, more difficult changes.
You don't need a record per field. Entities have a natural serialization format, Protocol Buffers, and you can simply store the entity as an encoded Protocol Buffer in the audit record. If you're operating at the model level, use model_to_protobuf to convert a model into a Protocol Buffer.
All of the above are far more easily applied to storing the record after it's modified, rather than before it was changed. This shouldn't be an issue, though - if you need the record before it was modified, you can just go back one entry in the audit log.
I am bit out of touch of GAE and also no sdk with me to test it out, so here is some guidelines to given you a hint what you may do.
Create a metaclass AuditMeta which you set in any models you want audited
AuditMeta while creating a new model class should copy Class with new name with "_audit" appended and should also copy the attribute too, which becomes a bit tricky on GAE as attributes are itself descriptors
Add a put method to each such class and on put create a audit object for that class and save it, that way for each row in tableA you will have history in tableA_audit
e.g. a plain python example (without GAE)
import new
class AuditedModel(object):
def put(self):
print "saving",self,self.date
audit = self._audit_class()
audit.date = self.date
print "saving audit",audit,audit.date
class AuditMeta(type):
def __new__(self, name, baseclasses, _dict):
# create model class, dervied from AuditedModel
klass = type.__new__(self, name, (AuditedModel,)+baseclasses, _dict)
# create a audit class, copy of klass
# we need to copy attributes properly instead of just passing like this
auditKlass = new.classobj(name+"_audit", baseclasses, _dict)
klass._audit_class = auditKlass
return klass
class MyModel(object):
__metaclass__ = AuditMeta
date = "XXX"
# create object
a = MyModel()
a.put()
output:
saving <__main__.MyModel object at 0x957aaec> XXX
saving audit <__main__.MyModel_audit object at 0x957ab8c> XXX
Read audit trail code , only 200 lines, to see how they do it for django