Skip to content

Commit 0b31e02

Browse files
charettesfelixxm
authored andcommitted
Fixed #33618 -- Fixed MTI updates outside of primary key chain.
1 parent 9ffd4ea commit 0b31e02

File tree

3 files changed

+34
-3
lines changed

3 files changed

+34
-3
lines changed

django/db/models/sql/compiler.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,7 +1836,23 @@ def pre_sql_setup(self):
18361836
query.clear_ordering(force=True)
18371837
query.extra = {}
18381838
query.select = []
1839-
query.add_fields([query.get_meta().pk.name])
1839+
meta = query.get_meta()
1840+
fields = [meta.pk.name]
1841+
related_ids_index = []
1842+
for related in self.query.related_updates:
1843+
if all(
1844+
path.join_field.primary_key for path in meta.get_path_to_parent(related)
1845+
):
1846+
# If a primary key chain exists to the targeted related update,
1847+
# then the meta.pk value can be used for it.
1848+
related_ids_index.append((related, 0))
1849+
else:
1850+
# This branch will only be reached when updating a field of an
1851+
# ancestor that is not part of the primary key chain of a MTI
1852+
# tree.
1853+
related_ids_index.append((related, len(fields)))
1854+
fields.append(related._meta.pk.name)
1855+
query.add_fields(fields)
18401856
super().pre_sql_setup()
18411857

18421858
must_pre_select = (
@@ -1851,10 +1867,13 @@ def pre_sql_setup(self):
18511867
# don't want them to change), or the db backend doesn't support
18521868
# selecting from the updating table (e.g. MySQL).
18531869
idents = []
1870+
related_ids = collections.defaultdict(list)
18541871
for rows in query.get_compiler(self.using).execute_sql(MULTI):
18551872
idents.extend(r[0] for r in rows)
1873+
for parent, index in related_ids_index:
1874+
related_ids[parent].extend(r[index] for r in rows)
18561875
self.query.add_filter("pk__in", idents)
1857-
self.query.related_ids = idents
1876+
self.query.related_ids = related_ids
18581877
else:
18591878
# The fast path. Filters and updates in one query.
18601879
self.query.add_filter("pk__in", query)

django/db/models/sql/subqueries.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def get_related_updates(self):
134134
query = UpdateQuery(model)
135135
query.values = values
136136
if self.related_ids is not None:
137-
query.add_filter("pk__in", self.related_ids)
137+
query.add_filter("pk__in", self.related_ids[model])
138138
result.append(query)
139139
return result
140140

tests/model_inheritance_regress/tests.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,3 +667,15 @@ def test_create_new_instance_with_pk_equals_none_multi_inheritance(self):
667667
Politician.objects.get(pk=c1.politician_ptr_id).title,
668668
"senator 1",
669669
)
670+
671+
def test_mti_update_parent_through_child(self):
672+
Politician.objects.create()
673+
Congressman.objects.create()
674+
Congressman.objects.update(title="senator 1")
675+
self.assertEqual(Congressman.objects.get().title, "senator 1")
676+
677+
def test_mti_update_grand_parent_through_child(self):
678+
Politician.objects.create()
679+
Senator.objects.create()
680+
Senator.objects.update(title="senator 1")
681+
self.assertEqual(Senator.objects.get().title, "senator 1")

0 commit comments

Comments
 (0)