1

I'm using Django create_or_update function.

In case of update, Is there a way to know the list of changed fields.

Obviously I can use the get_or_create function before and in case, after this, I can update the model.. but I'm looking for a way to have this using a single query.

Is it possible?

Safari
  • 11,437
  • 24
  • 91
  • 191
  • I think its update_or_create no? and I looked into the django code for update_or_create and it does not have any functionality to tell updated fields using a single query. [reference](https://github.com/django/django/blob/master/django/db/models/query.py#L578) – Sami Jun 18 '20 at 18:56

2 Answers2

1

update_or_create(defaults=None, **kwargs)

The update_or_create method tries to fetch an object from database based on the given kwargs. If a match is found, it updates the fields passed in the defaults dictionary.

The query doesn't care if the updated fields have changed or not, all the field in "default" are updated

Returns a tuple of (object, created), where object is the created or updated object and created is a boolean specifying whether a new object was created.

https://docs.djangoproject.com/en/3.0/ref/models/querysets/#update-or-create

Houda
  • 671
  • 6
  • 16
1

Maybe it's not the best solution but I think it gets the job done. You could retrieve the instance that will be updated and then compute the fields that have changed using filter() and lambda functions, as suggested in this answer by Rahul Gupta.

Let's suppose you can identify the instance through say first_name and last_name as reported in the docs:

old_instance = Person.objects.filter(first_name=first_name, last_name=last_name)
old_instance = old_instance[0] if old_instance else None

new_instance, created = Person.objects.update_or_create(
    first_name=first_name, last_name=last_name,
    defaults={'first_name': 'Bob'},
)

# it's been updated and we have the old instance
if not created and old_instance:
    # get a list of the model's fields
    fields = Person._meta.get_all_field_names()
    # compute the fields which have changed
    diff_fields = filter(lambda field: getattr(old_instance,field,None)!=getattr(new_instance,field,None), fields)

The diff_fields list at this point should only contain first_name.