Opened 9 days ago
Last modified 2 days ago
#35890 new Bug
pre_save field in parent models are not called during update in update_or_create
Reported by: | Gagaro | Owned by: | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Gagaro, Simon Charette, Florian Apolloner, Sarah Boyce | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
With the following models:
class Parent(models.Model): modified_at = models.DateTimeField(auto_now=True) class Child(Parent): modified_at_child = models.DateTimeField(auto_now=True)
When calling update_or_create
, the pre_save of the modified_at
field is not called, and the field is not updated:
# Create >>> instance, _ = Child.objects.update_or_create(pk=1) >>> instance.modified_at datetime.datetime(2024, 11, 6, 14, 1, 11, 52881, tzinfo=datetime.timezone.utc) >>> instance.modified_at_child datetime.datetime(2024, 11, 6, 14, 1, 11, 54363, tzinfo=datetime.timezone.utc) # Update >>> instance, _ = Child.objects.update_or_create(pk=1) >>> instance.modified_at datetime.datetime(2024, 11, 6, 14, 1, 11, 52881, tzinfo=datetime.timezone.utc) >>> instance.modified_at_child datetime.datetime(2024, 11, 6, 14, 1, 21, 517245, tzinfo=datetime.timezone.utc)
The regression seems to have happened in https://github.com/django/django/commit/6cc0f22a73970dd7c0d29d4d8d2ff9e1cc862b30
Using self.model._meta.concrete_fields
instead of self.model._meta.local_concrete_fields
seems to solve the issue, but I don't know the internal API well enough to understand all of the implications.
Change History (7)
comment:1 by , 9 days ago
Has patch: | set |
---|
comment:2 by , 9 days ago
Description: | modified (diff) |
---|
comment:3 by , 9 days ago
Patch needs improvement: | set |
---|---|
Triage Stage: | Unreviewed → Accepted |
comment:4 by , 9 days ago
Why do you think it should be that way?
In our cases, we have an auto_now
field on the parent model only, and we need it to be updated every time, even if there is only a single change in a field from a child model.
comment:5 by , 8 days ago
I had more thoughts about it, and from a user perspective, I think having some fields but not others updated would be an unexpected behavior. The fields on the parent model are parts of the child one, it is transparent when we try to access or change them on the instance. I'd expect them to be updated in the same way the child ones are.
comment:6 by , 2 days ago
Cc: | added |
---|
I think we need someone else to chime in here to get a consensus before moving forward with either solution.
The Field.auto_now
pattern is a feature on its way out #22995 and as it doesn't play nicely with update_fields
#22981 particularly in the context of update_or_create
(#35014 and associated forum discussion).
In your particular case, Child.save(update_fields={...})
, won't update "modified_at"
unless it's specified explicitly so your expectations are already broken for any direct or indirect (through third-party apps) usage of save(update_fields)
; update_or_create
is only a single instance of that.
The changes made to handle local concrete fields in #32095 (original PR by Florian, eventually merged changes by Sarah) unfortunately it didn't cover this use case, even if it was pointed out as a point of ambiguity, as neither Book
or Publisher
are using MTI. Not to do any finger pointing here, I should have caught in the follow up PR and be more explicit about what I actually meant. I just wanted to point out that it was at least discussed at the time.
Why do you think it should be that way?
From my perspective, given the intent #32095 was to avoid unnecessary field updates, performing MTI inherited tables updates when non of their fields are explicitly requested to be updated because they use a pattern meant to be deprecated is not the right approach.
comment:7 by , 2 days ago
Indeed, I see your point.
I didn't know that auto_now
was supposed to be deprecated. It isn't obvious from the documentation either.
I see no reasons why this should only work for local concrete fields given
update_or_create
works with MTI.There's a nuance not captured in the patch though that I believe should be addressed at the same time. Only pre-save fields for the model associated with at-least-one update field should be augmented. What I mean by that is that given the following models
Then