#27335 closed Cleanup/optimization (wontfix)
Avoid object save during QuerySet.update_or_create() when there were no changes
Reported by: | Artem Skoretskiy | Owned by: | Andrew Nester |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Right now when you use QuerySet.update_or_create -- it always saves into DB even if there were no changes.
It would be great that if nothing is changed -- nothing is saved into DB. I found myself often reusing the same pattern in our projects.
Use case:
>>> from django.contrib.auth.models import User >>> User.objects.create(is_superuser=True, username='admin', is_active=True) # generate a use in DB to work with >>> # later in our code >>> user, created = User.objects.update_or_create(defaults={'is_active': True}, username='admin') # here an extra DB write though no data were changed
If you check the current code in master (https://github.com/django/django/blob/master/django/db/models/query.py#L477):
try: obj = self.select_for_update().get(**lookup) except self.model.DoesNotExist: obj, created = self._create_object_from_params(lookup, params) if created: return obj, created for k, v in six.iteritems(defaults): setattr(obj, k, v() if callable(v) else v) obj.save(using=self.db) # HERE IT IS => always save
If object exists in DB, it would apply all attributes from default
and save it even if there were no changes in values.
We could do something like that:
changed = False for k, v in six.iteritems(defaults): v = v() if callable(v) else v if v != getattr(obj, k): modified = True setattr(obj, k, v) if changed: obj.save(using=self.db) # save only if there were changes
If you think it is too complex for all use cases, we may put a flag for enabling / disabling this feature or have another method for this logic.
Change History (5)
comment:1 by , 8 years ago
Summary: | Avoid object save during QuerySet.update_or_create when there were no changes → Avoid object save during QuerySet.update_or_create() when there were no changes |
---|---|
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 8 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:4 by , 8 years ago
Resolution: | → wontfix |
---|---|
Status: | assigned → closed |
Closing in absence of further discussion.
comment:5 by , 6 years ago
I'd like to see some kind of solution (or workaround) for this -- particularly because there isn't a workaround that I can see -- except maybe putting this logic in my models save(), but then it's always active. I guess a temporary proxy model might work as well.
I have a use case where I am synchronizing two databases with different schemas as part of a migration. The sync takes a long time when updating the data even when the data is the same because it's forcing an update for data that is already synced.
I'm for making the behavior optional, though ...
Hi @tonnzorr
As discussed in pull request linked to this ticket implementing this optimization might cause some issues not covered here previously. Some of them:
My point here is that implementing this feature requires a lot of time comparing with profit we can gain from it, so it looks like we are trying to be too smart here and it's a little bit dangerous because this optimization looks like source of potential regression.
I am just curious to know your opinion about it because my point here is that it's better to leave behavior of update_or_create as it is now.