You can use this mixin that can be used to detect which fields have changed in a model instance and pass that information to the save method. Here is a description of the mixin:
- In the init method, the mixin initializes a dictionary called old_instance_data which stores the initial values of the fields in the model instance.
- In the save method, the mixin compares the current values of the fields with the initial values stored in old_instance_data to determine which fields have changed. It creates a set of update_fields containing the names of the changed fields.
- If the model instance has a primary key (self.pk is not None), the mixin automatically determines the changed fields if update_fields is not explicitly provided.
- The mixin then calls the save method of the parent class, passing the update_fields to it, along with any other keyword arguments that may be provided.
- If you pass your own update_fields parameter when saving, it use this one. Thus you can partially use other update_fields anywhere in your code.
By using this mixin in a model, you can conveniently track and save only the fields that have been modified, which can be helpful for optimizing database updates and reducing unnecessary database queries.
from django.db import models
class UpdateFieldsMixin(models.Model):
""" Detects which field changed and passes it to save method """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
fields = (field.name for field in self._meta.fields)
self.old_instance_data = {name: getattr(self, name, None)
for name in fields}
def save(self, update_fields=None, **kwargs):
if self.pk:
update_fields = update_fields or {
k for k, v in self.old_instance_data.items()
if getattr(self, k, None) != v}
return super().save(update_fields=update_fields, **kwargs)
class Meta:
abstract = True
Use it like:
class YourModel(UpdateFieldsMixin, models.Model):
# Any code
def save(**kwargs):
# Any code
return super().save(**kwargs)
That's all. If you have a custom save method, make sure you call UpdateFieldsMixin.save. I'd suggest using super() like in the example above.
.update()
sound deprecated, both update methods are still available and non-deprecated as of Django 4.0. Great answer for mentioning both options, though :-) – Alston