Here an easy example to show the problem:
{{{
class Base(models.Model):
base_id = models.AutoField(primary_key=True)
field_base = models.IntegerField()
class OtherBase(models.Model):
otherbase_id = models.AutoField(primary_key=True)
field_otherbase = models.IntegerField()
class Child(Base, OtherBase):
pass
}}}
Then in django shell:
{{{
In [1]: OtherBase.objects.create(field_otherbase=100)
<QuerySet [{'otherbase_id': 1, 'field_otherbase': 100}]>
In [2]: OtherBase.objects.create(field_otherbase=101)
<QuerySet [{'otherbase_id': 2, 'field_otherbase': 101}]>
In [3]: Child.objects.create(field_base=0, field_otherbase=0)
<Child: Child object (1)>
In [4]: Child.objects.create(field_base=1, field_otherbase=1)
<Child: Child object (2)>
In [5]: Child.objects.update(field_otherbase=55)
SELECT "appliances_child"."base_ptr_id"
FROM "appliances_child"
Execution time: 0.000647s [Database: default]
UPDATE "appliances_otherbase"
SET "field_otherbase" = 55
WHERE "appliances_otherbase"."otherbase_id" IN (1, 2)
Execution time: 0.001414s [Database: default]
Out[5]: 2
In [6]: Child.objects.values('field_otherbase')
<QuerySet [{'field_otherbase': 0}, {'field_otherbase': 1}]>
In [7]:
OtherBase.objects.filter(otherbase_id__in=[1,2]).values('field_otherbase')
<QuerySet [{'field_otherbase': 55}, {'field_otherbase': 55}]>
}}}
As seen on the above code, updating `Child` fields from second parent has
no effect. Worse is that `OtherBase` fields where modifed because query is
using primiary keys from `Base` class.
--
Ticket URL: <https://code.djangoproject.com/ticket/33618>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* cc: Sonicold (added)
* needs_docs: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:1>
* owner: nobody => Simon Charette
* status: new => assigned
* stage: Unreviewed => Accepted
Comment:
Thank you for your report. Confirmed this is another issue with concrete
MTI.
I've looked at the code and in order to address the issue both
`sql.UpdateQuery` and `sql.SQLUpdateCompiler` need to be updated.
The changes revolve around changing `UpdateQuery.related_ids: list[int]`
to `related_ids: dict[Model, list[int]]` and making sure
`sql.SQLUpdateCompiler.pre_sql_setup` populates `query.related_ids` by the
`parent_link` of the `related_updates`.
[https://github.com/django/django/blob/1a7d75cf77639e450854d9bcf9518664f755eb04/django/db/models/sql/compiler.py#L1839
That means not only selecting the primary key] of the parent model but all
parent link values of child model fields being updated.
From there `get_related_updates` can be updated to do
`query.add_filter("pk__in", self.related_ids[model])` instead.
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:2>
Comment (by Simon Charette):
I think https://github.com/django/django/pull/15563 should do. Could you
confirm Sonicold.
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:3>
* needs_docs: 1 => 0
* has_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:4>
Comment (by Sonicold):
Replying to [comment:3 Simon Charette]:
> I think https://github.com/django/django/pull/15563 should do. Could you
confirm Sonicold.
Thank you Simon Charette for your quick answer and fix. It works well with
the example I have posted and also with a more complex model with many
more inheritances.
Do you know if this patch will be backported in older django versions ?
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:5>
Comment (by Simon Charette):
> Do you know if this patch will be backported in older django versions ?
I'll let Mariusz and Carlton chime in here. This is a data loss issue so
[https://docs.djangoproject.com/en/4.0/internals/release-process/ it
should theoretically qualify] but the issue has been around forever and
only happens for uncommon model structures so I'd be wary of introducing a
regression via a minor version if this was backported and possibly cause a
different kind of data loss issue.
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:6>
Comment (by Mariusz Felisiak):
> I'll let Mariusz and Carlton chime in here. This is a data loss issue so
[https://docs.djangoproject.com/en/4.0/internals/release-process/ it
should theoretically qualify] but the issue has been around forever and
only happens for uncommon model structures so I'd be wary of introducing a
regression via a minor version if this was backported and possibly cause a
different kind of data loss issue.
IMO, the risk of introducing another regression is too great. Moreover it
was reported after Django 2.2 EOL (April, 1st) so it wouldn't be
backported regardless of our decision.
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:7>
* stage: Accepted => Ready for checkin
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:8>
Comment (by Carlton Gibson):
Just commenting for completeness:
> ... the issue has been around forever and only happens for uncommon
model structures so I'd be wary of introducing a regression via a minor
version if this was backported and possibly cause a different kind of data
loss issue.
I think this is the dominant point. I don't think we should backport. (So
I agree with Mariusz.)
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:9>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"0b31e024873681e187b574fe1c4afe5e48aeeecf" 0b31e024]:
{{{
#!CommitTicketReference repository=""
revision="0b31e024873681e187b574fe1c4afe5e48aeeecf"
Fixed #33618 -- Fixed MTI updates outside of primary key chain.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:10>
Comment (by Sonicold):
Replying to [comment:9 Carlton Gibson]:
> Just commenting for completeness:
>
> > ... the issue has been around forever and only happens for uncommon
model structures so I'd be wary of introducing a regression via a minor
version if this was backported and possibly cause a different kind of data
loss issue.
>
> I think this is the dominant point. I don't think we should backport.
(So I agree with Mariusz.)
Ok understood ! Do you think it will be added to next minor release
4.04/4.05 or in 4.1 ? Is there a big risk for us to patch manually the 4.x
?
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:11>
Comment (by Carlton Gibson):
It would be Django 4.1.
> Is there a big risk for us to patch manually the 4.x ?
Assuming you have good tests you could fork, apply
0b31e024873681e187b574fe1c4afe5e48aeeecf to a branch from `stable/4.0.x`.
That's essentially how we'd backport ourselves, by cherrypicking the
commit to the release branch.
To maintain that you would need to update your fork and rebase after each
4.0 point-release, but that's certainly do-able.
You could then install via [https://pip.pypa.io/en/stable/topics/vcs-
support/ Pip's VCS support].
I can't judge the degree of ''risk'' per se, it depends on your team, and
whether time is allocated for any issues that may arise.
(Part of the reasoning behind the backport policy is that release branches
should be largely stable though.)
I hope that helps.
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:12>
Comment (by Sonicold):
Replying to [comment:12 Carlton Gibson]:
> It would be Django 4.1.
>
> > Is there a big risk for us to patch manually the 4.x ?
>
> Assuming you have good tests you could fork, apply
0b31e024873681e187b574fe1c4afe5e48aeeecf to a branch from `stable/4.0.x`.
> That's essentially how we'd backport ourselves, by cherrypicking the
commit to the release branch.
>
> To maintain that you would need to update your fork and rebase after
each 4.0 point-release, but that's certainly do-able.
>
> You could then install via [https://pip.pypa.io/en/stable/topics/vcs-
support/ Pip's VCS support].
>
> I can't judge the degree of ''risk'' per se, it depends on your team,
and whether time is allocated for any issues that may arise.
> (Part of the reasoning behind the backport policy is that release
branches should be largely stable though.)
>
> I hope that helps.
>
>
Yes that helped, thanks a lot !
--
Ticket URL: <https://code.djangoproject.com/ticket/33618#comment:13>