renno
renno

Reputation: 2827

How to track changes when using update() in Django models

I'm trying to keep track of the changes whenever a field is changed.

I can see the changes in Django Admin History whenever I use the .save() method, but whenever I use the .update() method it does not record whatever I changed in my object.

I want to use update() because it can change multiple fields at the same time. It makes the code cleaner and more efficient (one query, one line...)

Right now I'm using this:

u = Userlist.objects.filter(username=user['username']).update(**user)

I can see all the changes when I do

u = Userlist.objects.get(username=user['username'])
u.lastname=lastname
u.save()

I'm also using django-simple-history to see the changes.setup.

Upvotes: 3

Views: 5079

Answers (2)

jurms22
jurms22

Reputation: 121

Here's how I've handled this and it's worked well so far:

# get current model instance to update
instance = UserList.objects.get(username=username)

# use model_to_dict to convert object to dict (imported from django.forms.models import model_to_dict)
obj_dict = model_to_dict(instance)

# create instance of the model with this old data but do not save it
old_instance = UserList(**obj_dict)

# update the model instance (there are multiple ways to do this)
UserList.objects.filter(username=username).update(**user) 

# get the updated object
updated_object = UserList.objects.get(id=id)

# get list of fields in the model class
my_model_fields = [field.name for field in cls._meta.get_fields()]

# get list of fields if they are different
differences = list(filter(lambda field: getattr(updated_object, field, None)!= getattr(old_instance, field, None), my_model_fields))

The differences variable will give you the list of fields that are different between the two instances. I also found it helpful to add which model fields I don't want to check for differences (e.g. we know the updated_date will always be changed, so we don't need to keep track of it).

skip_diff_fields = ['updated_date']

my_model_fields = []
for field in cls._meta.get_fields():
    if field.name not in skip_diff_fields:
        my_model_fields.append(field.name)

Upvotes: 0

Moses Koledoye
Moses Koledoye

Reputation: 78554

From the docs:

Finally, realize that update() does an update at the SQL level and, thus, does not call any save() methods on your models, nor does it emit the pre_save or post_save signals (which are a consequence of calling Model.save())

update() works at the DB level, so Django admin cannot track changes when updates are applied via .update(...).

If you still want to track the changes on updates, you can use:

for user in Userlist.objects.filter(age__gt=40):
    user.lastname = 'new name'
    user.save()

This is however more expensive and is not advisable if the only benefit is tracking changes via the admin history.

Upvotes: 3

Related Questions