# update\_or\_create Behavior

**URL:** <https://forum.djangoproject.com/t/update-or-create-behavior/25944>\
**Category:** ORM\
**Created:** [December 6, 2023, 1:17pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944 "2023-12-06T13:17:24Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![apollo13](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/apollo13/32/30_2.png) [@apollo13](https://forum.djangoproject.com/u/apollo13)\
**Post date:** [December 7, 2023, 9:33am UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/22 "2023-12-07T09:33:34Z")

</div>

> [@JordanHyatt](#):
>
> But yeah lets deprecitate the update\_fields param!

Sorry? This is not on the table.

---

<div class="post-metadata">

**Author:** ![JordanHyatt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jordanhyatt/32/17597_2.png) [@JordanHyatt](https://forum.djangoproject.com/u/JordanHyatt)\
**Post date:** [December 7, 2023, 9:53am UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/23 "2023-12-07T09:53:14Z")

</div>

> [@apollo13](#):
>
> Sorry? This is not on the table.

My bad, I thought we put it on the table

---

<div class="post-metadata">

**Author:** ![sarahboyce](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/sarahboyce/32/12734_2.png) [@sarahboyce](https://forum.djangoproject.com/u/sarahboyce)\
**Post date:** [December 7, 2023, 10:11am UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/24 "2023-12-07T10:11:28Z")

</div>

> [@JordanHyatt](#):
>
> But there could still be others out there desiring an opt out!

It would be very nice of you to share a minimal code sample of what you have implemented in your own project to achieve the opt out mechanism. You can also add a link to that comment to your original ticket on Trac so people can view this discussion. 👍

---

<div class="post-metadata">

**Author:** ![apollo13](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/apollo13/32/30_2.png) [@apollo13](https://forum.djangoproject.com/u/apollo13)\
**Post date:** [December 7, 2023, 10:14am UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/25 "2023-12-07T10:14:57Z")

</div>

What I was suggesting was to introduce a setting that let’s you delay the new behavior for one version or so.

---

<div class="post-metadata">

**Author:** ![HamaBarhamou](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/hamabarhamou/32/18007_2.png) [@HamaBarhamou](https://forum.djangoproject.com/u/HamaBarhamou)\
**Post date:** [December 7, 2023, 10:54am UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/26 "2023-12-07T10:54:15Z")

</div>

Hello everyone,

Following the ongoing discussion about the changes made to `update_or_create` in Django 4.2, and considering my current work on extending the `bulk_create` method to integrate `F` and `Q` expressions (see [Ticket #34277](https://code.djangoproject.com/ticket/34277) and the related [GitHub Pull Request](https://github.com/django/django/pull/17515)), I would like to propose a similar enhancement for `update_or_create`.

The idea is to enable `update_or_create` to handle `F` and `Q` expressions. This would offer greater flexibility and allow for more dynamic and conditional update or creation operations. For instance, it could enable updating fields based on their current values or external conditions, or creating new objects with attributes determined by complex conditions.

Extending `update_or_create` in this way could potentially address some of the issues raised by the current changes in Django 4.2, offering more control and precision in managing object updates and creations.

I am eager to hear your thoughts on this proposal and whether this could be an interesting avenue to explore for enhancing the functionality of `update_or_create` in Django.

Thank you for your thoughts and suggestions.

Best regards,

---

<div class="post-metadata">

**Author:** ![JordanHyatt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jordanhyatt/32/17597_2.png) [@JordanHyatt](https://forum.djangoproject.com/u/JordanHyatt)\
**Post date:** [December 7, 2023, 11:42am UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/27 "2023-12-07T11:42:53Z")

</div>

> [@apollo13](#):
>
> What I was suggesting was to introduce a setting that let’s you delay the new behavior for one version or so.

Yeah sorry, I was being ambitious/facetious about the update\_fields thing. I think adding a setting bypass would be a nice addition to help people in a similar situation to me make the transition to 4.2

---

<div class="post-metadata">

**Author:** ![JordanHyatt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jordanhyatt/32/17597_2.png) [@JordanHyatt](https://forum.djangoproject.com/u/JordanHyatt)\
**Post date:** [December 7, 2023, 12:06pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/28 "2023-12-07T12:06:45Z")

</div>

Another question I want to ask on this topic. Documentation states with passing update\_fields “There will be a slight performance benefit from preventing all of the model fields from being updated in the database”. Was the behavior change of update\_or\_create in 4.2 solely to take advantage of this “slight” performance increase?

---

<div class="post-metadata">

**Author:** ![kgeidel](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kgeidel/32/17603_2.png) [@kgeidel](https://forum.djangoproject.com/u/kgeidel)\
**Post date:** [December 7, 2023, 12:12pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/29 "2023-12-07T12:12:55Z")

</div>

> [@charettes](#):
>
> Why can’t the `save` method of such models be adapted to augment `update_fields` with the dependant fields instead?

It would be nice to not have to go back over 10s of thousands of lines of code. Upgrading a central package like Django will inevitably need some patching but this seems like an opportunity to mitigate a big one with a few lines of code that will preserve the functionality of the change introduced in 4.2 but provide options for backwards compatibility with global search or replace versus reworking every extended save method.

---

<div class="post-metadata">

**Author:** ![kgeidel](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kgeidel/32/17603_2.png) [@kgeidel](https://forum.djangoproject.com/u/kgeidel)\
**Post date:** [December 7, 2023, 12:14pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/30 "2023-12-07T12:14:10Z")

</div>

and to be clear, I’m all for efficiency gains. I will start using update\_fields in my update\_or\_create()s moving forward for sure!

> [@sarahboyce](#):
>
> create a model mixin with a custom object manager that has an overwritten `update_or_create`

I’ll try this and report back! I would like to use latest version so if this can help my bypass this new forced behavior its worth a spin.

> [@apollo13](#):
>
> When the whole object is updated all other requests that updated parts of the object in the meantime (like incrementing a view count by one) will have their effects reversed.

Isn’t there all sorts of things that could cause this? This would never happen in my project or any project using ACID compliant databases. If you are many concurrent writes and require atomic transactions wouldn’t you want to address that at the database level instead of changing the behavior of manager methods like update\_or\_create when there are many other scenarios in which you would still have data loss problems?

---

<div class="post-metadata">

**Author:** ![JordanHyatt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jordanhyatt/32/17597_2.png) [@JordanHyatt](https://forum.djangoproject.com/u/JordanHyatt)\
**Post date:** [December 7, 2023, 12:32pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/31 "2023-12-07T12:32:36Z")

</div>

> [@sarahboyce](#):
>
> It would be very nice of you to share a minimal code sample of what you have implemented in your own project to achieve the opt out mechanism. You can also add a link to that comment to your original ticket on Trac so people can view this discussion. 👍

Here is our CustomManager solution:

```auto
class CustomManager(Manager):
    """ Custom Manager that by default reverts the behavior of update_or_create to < 4.2 behavior and adds ability to pass extra kwargs to save """
    def update_or_create(self, defaults=None, extra_save_kwargs=None, use_update_fields=False, **kwargs):
        """
        Look up an object with the given kwargs, updating one with defaults
        if it exists, otherwise create a new one.
        Return a tuple (object, created), where created is a boolean
        specifying whether an object was created.
        """
        defaults = defaults or {}
        extra_save_kwargs = extra_save_kwargs or {}
        self._for_write = True
        with transaction.atomic(using=self.db):
            # Lock the row so that a concurrent update is blocked until
            # update_or_create() has performed its save.
            obj, created = self.select_for_update().get_or_create(defaults, **kwargs)
            if created:
                return obj, created
            for k, v in resolve_callables(defaults):
                setattr(obj, k, v)

            update_fields = set(defaults)
            concrete_field_names = self.model._meta._non_pk_concrete_field_names
            # update_fields does not support non-concrete fields.
            if concrete_field_names.issuperset(update_fields) and use_update_fields:
                # Add fields which are set on pre_save(), e.g. auto_now fields.
                # This is to maintain backward compatibility as these fields
                # are not updated unless explicitly specified in the
                # update_fields list.
                for field in self.model._meta.local_concrete_fields:
                    if not (
                        field.primary_key or field. __class__.pre_save is Field.pre_save
                    ):
                        update_fields.add(field.name)
                        if field.name != field.attname:
                            update_fields.add(field.attname)
                obj.save(using=self.db, update_fields=update_fields, **extra_save_kwargs)
            else:
                obj.save(using=self.db, **extra_save_kwargs)
        return obj, False

```

---

<div class="post-metadata">

**Author:** ![apollo13](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/apollo13/32/30_2.png) [@apollo13](https://forum.djangoproject.com/u/apollo13)\
**Post date:** [December 7, 2023, 12:33pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/32 "2023-12-07T12:33:14Z")

</div>

It was not solely for performance but also for correctness. Updating all fields is simply wrong in concurrent scenarios in my experience. When the whole object is updated all other requests that updated parts of the object in the meantime (like incrementing a view count by one) will have their effects reversed. One could argue that this is a dataloss bug at the framework level. I for one was very much surprised when I figured out that the whole object was updated.

---

<div class="post-metadata">

**Author:** ![JordanHyatt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jordanhyatt/32/17597_2.png) [@JordanHyatt](https://forum.djangoproject.com/u/JordanHyatt)\
**Post date:** [December 7, 2023, 12:40pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/33 "2023-12-07T12:40:18Z")

</div>

> [@apollo13](#):
>
> It was not solely for performance but also for correctness. Updating all fields is simply wrong in concurrent scenarios in my experience. When the whole object is updated all other requests that updated parts of the object in the meantime (like incrementing a view count by one) will have their effects reversed. One could argue that this is a dataloss bug at the framework level. I for one was very much surprised when I figured out that the whole object was updated.

I’m not sure I am following you. The default behavior of save is update\_fields=None meaning the entire object is re-written to the DB. update\_or\_create is now calling save in its non-default configuration. So, you are saying anytime I call save() manually using the defaults I am at risk of data-loss?

---

<div class="post-metadata">

**Author:** ![JordanHyatt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jordanhyatt/32/17597_2.png) [@JordanHyatt](https://forum.djangoproject.com/u/JordanHyatt)\
**Post date:** [December 7, 2023, 12:58pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/34 "2023-12-07T12:58:28Z")

</div>

> **[Django](https://docs.djangoproject.com/en/4.2/ref/models/instances/#ref-models-update-fields)**
>
> The web framework for perfectionists with deadlines.

" If `update_fields` is passed in, only the [`pre_save()`](https://docs.djangoproject.com/en/4.2/ref/models/fields/#django.db.models.Field.pre_save) methods of the `update_fields` are called. For example, this means that date/time fields with `auto_now=True` will not be updated unless they are included in the `update_fields` ."

Does update\_or\_create now break auto\_now?

---

<div class="post-metadata">

**Author:** ![apollo13](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/apollo13/32/30_2.png) [@apollo13](https://forum.djangoproject.com/u/apollo13)\
**Post date:** [December 7, 2023, 1:09pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/35 "2023-12-07T13:09:41Z")

</div>

Yes, depending on how your application works every call to `save()` is a potential data loss. One could say this might be expected for `save()` since it’s purpose is to save the whole model usually. For `update_or_create()` it is less clear. There is technically not even a need to call `.save()`, it could just as well utilize `create()` or `update()`. The docs don’t even guarantee that `.save()` would be called at all (it is but that can be considered an implementation detail).

Sidenote: the only case where `save()` is actually safe in the sense that it does something without possibly breaking something else is when it creates a new object or is used in conjunction with `select_for_update` (though only if you don’t use “SELECT FOR UPDATE OF …”.

Another way around this is to have opportunistic updates. There you essentially have a version column and each and every update includes “set version = version + 1 where version = known\_version\_we\_are\_upfating\_from”. If this update then return zero affected rows you know that another request updated your object in the meantime. Whether your data is important enough to use this depends on your application.

---

<div class="post-metadata">

**Author:** ![charettes](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/charettes/32/27_2.png) [@charettes](https://forum.djangoproject.com/u/charettes)\
**Post date:** [December 7, 2023, 1:11pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/36 "2023-12-07T13:11:24Z")

</div>

No, please [refer to the patch](https://github.com/django/django/commit/6cc0f22a73970dd7c0d29d4d8d2ff9e1cc862b30#diff-d58ef61559dc7af5fdf7b56fee13571a4d2948e784cd608f6afeacf3ac2fb195R967-R984) which explicitly check for `pre_save` overrides when building the `update_fields` set hence why defining _derived_ fields with a `pre_save` override, such as the ones with `auto_now` just works.

This is why a `pre_save` override was suggested in a previous reply

> [@charettes](#):
>
> Another solution that hasn’t been discussed here, and would not require a `save` override, is to make sure that the derived fields properly implement the [pre\_save](https://docs.djangoproject.com/en/5.0/ref/models/fields/#django.db.models.Field.pre_save) interface which would ensure they [get added implicitly to the `update_fields` set](https://github.com/django/django/blame/00ef74376e382cf33d6a9529bb9fc1b412e0bc7d/django/db/models/query.py#L1001-L1003).

---

<div class="post-metadata">

**Author:** ![JordanHyatt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jordanhyatt/32/17597_2.png) [@JordanHyatt](https://forum.djangoproject.com/u/JordanHyatt)\
**Post date:** [December 7, 2023, 1:24pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/37 "2023-12-07T13:24:40Z")

</div>

> [@charettes](#):
>
> No, please [refer to the patch](https://github.com/django/django/commit/6cc0f22a73970dd7c0d29d4d8d2ff9e1cc862b30#diff-d58ef61559dc7af5fdf7b56fee13571a4d2948e784cd608f6afeacf3ac2fb195R967-R984) which explicitly check for `pre_save` overrides when building the `update_fields` set hence why defining _derived_ fields with a `pre_save` override, such as the ones with `auto_now` just works.

Ok, I understand, thank you for clarifying. But with this solution would require swapping out every single field (that you want excluded, which for us is a lot) with your custom Field Class that extends/overrides pre\_save, right?

---

<div class="post-metadata">

**Author:** ![JordanHyatt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jordanhyatt/32/17597_2.png) [@JordanHyatt](https://forum.djangoproject.com/u/JordanHyatt)\
**Post date:** [December 7, 2023, 1:32pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/38 "2023-12-07T13:32:05Z")

</div>

> [@apollo13](#):
>
> `update_or_create()` it is less clear. There is technically not even a need to call `.save()`, it could just as well utilize `create()` or `update()`. The docs don’t even guarantee that `.save()` would be called at all (it is but that can be considered an implementation detail).

Well if its not documented how it’s implemented, and the documentation makes no guarantees on its implementation, and the user has no power to influence it…seems like a risky method to use in general in its current state…

---

<div class="post-metadata">

**Author:** ![charettes](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/charettes/32/27_2.png) [@charettes](https://forum.djangoproject.com/u/charettes)\
**Post date:** [December 7, 2023, 1:40pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/39 "2023-12-07T13:40:31Z")

</div>

Correct, hence why the `save` override, either manual or though a model mixin as @sarahboyce suggested, or the usage of `GeneratedField` when possible seem like a better solution to me.

Using `pre_save` remains a good solution if you have a few _derived_ fields that have the exact same implementation for a few of your models.

---

<div class="post-metadata">

**Author:** ![apollo13](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/apollo13/32/30_2.png) [@apollo13](https://forum.djangoproject.com/u/apollo13)\
**Post date:** [December 7, 2023, 1:53pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/40 "2023-12-07T13:53:57Z")

</div>

That is a pessimistic take. The method does what it is documented to do - it creates or updates a row. For what it is worth basically no method in Django’s docs describe how they work internally.

---

<div class="post-metadata">

**Author:** ![JordanHyatt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jordanhyatt/32/17597_2.png) [@JordanHyatt](https://forum.djangoproject.com/u/JordanHyatt)\
**Post date:** [December 7, 2023, 2:19pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/41 "2023-12-07T14:19:35Z")

</div>

> [@apollo13](#):
>
> That is a pessimistic take. The method does what it is documented to do - it creates or updates a row. For what it is worth basically no method in Django’s docs describe how they work internally.

Yeah but update\_or\_create could also use QuerySet.update instead and would also accomplish the same result. Then then all of a sudden you would lose all of your post/pre save signals that you previously banked on running when using the update\_or\_create method. So to say the implementation doesn’t matter as long as it accomplishes its stated goal is not really fair. I’m not intending to be pessimisitic, but the example I just gave would have still broken a lot of code even if everyone was very careful about update\_fields handling

[Previous page](https://forum.djangoproject.com/t/update-or-create-behavior/25944.md?page=1)

[Next page](https://forum.djangoproject.com/t/update-or-create-behavior/25944.md?page=3)
