# 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:** 11\
**Page:** 3

<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, 3:55pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/42 "2023-12-07T15:55:33Z")

</div>

I agree with you in principle but Django has a very strict policy about only documented behavior being part of the public API contract. You are right that switching to `.update()` would break even more things and we try to keep backwards incompatible changes to a minimum so we wouldn’t try that. I understand that every change affects someone and in the worst case it really breaks a lot for some people. But if we would keep everything the same we could just freeze Django and stop developing it. In this case the performance and correctness gains seemed to be worth it (imo), and while annoying for you you can still work around the new behavior (if I understood correctly). Yes it might be more work for you and we try to keep it minimal, but sometimes it hits some people harder.

---

<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, 4:19pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/43 "2023-12-07T16:19:58Z")

</div>

> [@apollo13](#):
>
> I agree with you in principle but Django has a very strict policy about only documented behavior being part of the public API contract. You are right that switching to `.update()` would break even more things and we try to keep backwards incompatible changes to a minimum so we wouldn’t try that. I understand that every change affects someone and in the worst case it really breaks a lot for some people. But if we would keep everything the same we could just freeze Django and stop developing it. In this case the performance and correctness gains seemed to be worth it (imo), and while annoying for you you can still work around the new behavior (if I understood correctly). Yes it might be more work for you and we try to keep it minimal, but sometimes it hits some people harder.

This is fair. I appreciate you empathizing and I do understand that any change has the possibility to effect someone and that is sometime the cost of progress.

Yes I understand (and have already implemented) mitigation for the behavior change.

However, I still argue it would be valuable to give the user some control/flexibility over the manner in which update\_or\_create calls the objects save method. But if this breaks Django’s design rules or is just not that popular of a feature request because of my unique use case, I guess we have simply come to an impasse. I Appreciate everyone’s time discussing this matter.

---

<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, 4:34pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/44 "2023-12-07T16:34:01Z")

</div>

The main problem that I see with your suggestion is that there might be code in Django (like Simon pointed out) where you cannot control what is passed to save/update\_or\_create and we cannot expose this fully up the stack. So it feels like a half-baked solution to me (I fully understand that it might work in your specific case but it doesn’t feel like a solution that covers the 80% usecase)

---

<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, 6:46pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/45 "2023-12-07T18:46:29Z")

</div>

Yeah I suppose if update\_or\_create was exclusively a public method its a no brainer. Since it is apparently not, you would need to modify your model to be prepared to handle any method of being called anyway. I get it. I’m still grumpy but I get it.

---

<div class="post-metadata">

**Author:** ![gsilvapt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/gsilvapt/32/18538_2.png) [@gsilvapt](https://forum.djangoproject.com/u/gsilvapt)\
**Post date:** [January 19, 2024, 10:08am UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/46 "2024-01-19T10:08:51Z")

</div>

Hey everyone!

I recently created a ticket to report a problem I experienced regarding this same `update_or_create` and the fields selection for update: [#35125 (Parent model fields are ignored when preparing the update\_fields) – Django](https://code.djangoproject.com/ticket/35125)

# tl;dr:

Parent model fields are not picked up for update, meaning that if parent models contain `auto_now=True` fields, then they are **not** updated.

```python
class A(model.Model):
    updated_at = models.DateTimeField(auto_now=True)
    something = models.BooleanField(default=True)

# in another app
class B(A):
    name = models.CharField()

```

When `B.objects.update_or_create(name="x", defaults={"something": False})` is called, the `updated_at` field is not picked up for update, staying the same.

Took a look at the source code and [this](https://github.com/django/django/blob/4.2.3/django/db/models/query.py#L963) seems to be the troubling line. Perhaps it should read `for field in self.model._meta._non_pk_concrete_field_names` instead? I am unfamiliar with Django codebase, I am not sure if there is a better method to pick all model’s fields, including parent. I noticed the check before that line uses that attribute instead, which seems to pick up all model fields (including parent’s).

I can submit a patch if I get some thumbs up on this change. Not sureof any other repercussions of this change so I accept some guidance from someone more experience with the codebase to test this change.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 19, 2024, 12:13pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/47 "2024-01-19T12:13:15Z")

</div>

Side note: Please open up a new topic for this discussion. This is a different topic than the original post.

When you do so, please identify whether “A” is an abstract parent class or a concrete parent class. It would make a difference.

---

<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:** [January 19, 2024, 12:36pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/49 "2024-01-19T12:36:04Z")

</div>

> [@KenWhitesell](#):
>
> This is a different topic than the original post.

Is it a different topic though? Because it seems to me like it is another example of the change to update\_or\_create behavior having unintended consequences on legacy code. That fits quite well with the topic at hand here.

---

<div class="post-metadata">

**Author:** ![gsilvapt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/gsilvapt/32/18538_2.png) [@gsilvapt](https://forum.djangoproject.com/u/gsilvapt)\
**Post date:** [January 19, 2024, 12:36pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/50 "2024-01-19T12:36:15Z")

</div>

I was told in the ticket to mention in this specific topic as might be related - it seemed to me.

In the ticket I did mention it was not an abstract class but forgot here. However, the code does not mention being abstract because it isn’t.

If not related, I can open up a new discussion.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 19, 2024, 12:38pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/51 "2024-01-19T12:38:24Z")

</div>

Yes, please open up a different topic.

---

<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:** [January 19, 2024, 12:42pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/52 "2024-01-19T12:42:35Z")

</div>

> [@gsilvapt](#):
>
> I was told in the ticket to mention in this specific topic as might be related - it seemed to me.

I agree, it is related. Both issues point back to update\_or\_create changing its behavior on passing update\_fields which is clearly having unintended consequences.

---

<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:** [January 19, 2024, 2:14pm UTC](https://forum.djangoproject.com/t/update-or-create-behavior/25944/53 "2024-01-19T14:14:36Z")

</div>

Since the ticket has a small amount of new life I’ll make another interjection. I still haven’t heard an argument to the _upside_ of the behavior change besides the following:

1.) The very marginal performance improvement stated in the docs “There will be a slight performance benefit from preventing all of the model fields from being updated in the database”

and

2.) The principle argument of it feels more correct.

> [@apollo13](#):
>
> It was not solely for performance but also for correctness.

These upsides don’t seem to be worth the issues it appears to be causing, especially without providing any ability to bypass the new behavior.

Also let me ask this question to make my own principle argument. When you exclude fields from a ModelForm in an UpdateView, does it it make sure update\_fields has only the fields present in the form when the save is called? If not it seems like there is the same “performance” and “correctness” opportunity there. (for the record I am not suggesting to do this.)

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