3

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.

renno
  • 2,659
  • 2
  • 27
  • 58

2 Answers2

3

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.

Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
  • It's unfortunate that there is not a decorator to force a signal for this kind of behavior... – renno Jun 21 '16 at 18:31
  • I haven't come across any. The old hack works (see my update), but isn't advisable – Moses Koledoye Jun 21 '16 at 18:37
  • Yeah, I stored the fields I was changing in a list and then I iterated over then to change one by one. Finally I would save the new object. It is a little bit more computationally expensive and it is less cleaner, but I need to keep track of the changes... Thanks, Moses. – renno Jun 21 '16 at 18:43
0

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)
jurms22
  • 101
  • 1
  • 3