5

I've got a code like that:

# ... obj = Model.objects.get(pk=2342) if foo: obj.foo = 'bar' if bar: obj.bar = 'baz' obj.save() 

Is there a good way to find out if the model instance was modified in order to prevent saving it each time the code runs?

4 Answers 4

4

The typical pattern is to do something like:

model = Model.objects.get(pk=2342) dirty = False if foo: model.foo = 'bar' dirty = True if bar: model.bar = 'baz' dirty = True if dirty: model.save() 
Sign up to request clarification or add additional context in comments.

Comments

1

Just diff the 'snapshot' instance w/ current model instance field-by-field, you could get the snapshot through copy.copy(obj) or model_cls.objects.get(pk=obj.pk).

Also you could simply compare the dumped versions:

from django.core.serializers.json import Serializer dump = Serializer.serialize([obj]) ... changed = dump == Serializer.serialize([obj]) 

Normally, tweak your code is easiest:

obj = Model.objects.get(pk=2342) # 'obj' is better than 'model', IMO changed = False if foo: ... obj.foo = 'bar' changed = True if bar: ... obj.bar = 'baz' changed = True if changed: obj.save() 

Comments

0

Django performs this check internally. An sql query wont be fired unless the model has actually changed.

UPDATE

This answer is wrong. My bad. I confused this with something else.

4 Comments

+1. Leave that check to Django unless you have very compelling reasons to do so.
I don't think so. Can you provide some proof? Or have you mixed it up w/ has_changed checking of the Django form?
I don't think so either. Can you show on which line this check is handled here?
Furthermore, this logic is incorrect because it's the DB backend which maintains the consistency, not the model instance itself.
0

To do this in a more abstracted way, look at the documentation for the model method from_db(), which is a hook called when data is loaded into the model instance from the database. With this method, you can cache the original value of a field as it is loaded, and then compare that to the current value in the save() method.

Example (note, I haven't actually tested this code):

class SaveIfModified(models.Model): foo = models.TextField() @classmethod def from_db(cls, db, field_names, values): instance = super().from_db(db, field_names, values) # caveat: deferred values won't appear here if "foo" in field_names: instance._foo = values[field_names.index("foo")] return instance def save(self, *args, **kwargs): if self.foo == getattr(self, "_foo", None): return super().save(*args, **kwargs) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.