Skip to content

Commit e6108b7

Browse files
VIZZARD-Xcharettesjacobtylerwalls
committed
Fixed #36750 -- Made ordering of M2M objects deterministic in serializers.
Co-authored-by: Simon Charette <charette.s@gmail.com> Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
1 parent f991a18 commit e6108b7

3 files changed

Lines changed: 54 additions & 3 deletions

File tree

django/core/serializers/python.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,15 @@ def queryset_iterator(obj, field):
7777
chunk_size = (
7878
2000 if getattr(attr, "prefetch_cache_name", None) else None
7979
)
80-
return attr.iterator(chunk_size)
80+
query_set = attr.all()
81+
if not query_set.totally_ordered:
82+
current_ordering = (
83+
query_set.query.order_by
84+
or query_set.model._meta.ordering
85+
or []
86+
)
87+
query_set = query_set.order_by(*current_ordering, "pk")
88+
return query_set.iterator(chunk_size)
8189

8290
else:
8391

@@ -86,6 +94,13 @@ def m2m_value(value):
8694

8795
def queryset_iterator(obj, field):
8896
query_set = getattr(obj, field.name).select_related(None).only("pk")
97+
if not query_set.totally_ordered:
98+
current_ordering = (
99+
query_set.query.order_by
100+
or query_set.model._meta.ordering
101+
or []
102+
)
103+
query_set = query_set.order_by(*current_ordering, "pk")
89104
chunk_size = 2000 if query_set._prefetch_related_lookups else None
90105
return query_set.iterator(chunk_size=chunk_size)
91106

django/core/serializers/xml_serializer.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,15 @@ def queryset_iterator(obj, field):
177177
chunk_size = (
178178
2000 if getattr(attr, "prefetch_cache_name", None) else None
179179
)
180-
return attr.iterator(chunk_size)
180+
query_set = attr.all()
181+
if not query_set.totally_ordered:
182+
current_ordering = (
183+
query_set.query.order_by
184+
or query_set.model._meta.ordering
185+
or []
186+
)
187+
query_set = query_set.order_by(*current_ordering, "pk")
188+
return query_set.iterator(chunk_size)
181189

182190
else:
183191

@@ -186,6 +194,13 @@ def handle_m2m(value):
186194

187195
def queryset_iterator(obj, field):
188196
query_set = getattr(obj, field.name).select_related(None).only("pk")
197+
if not query_set.totally_ordered:
198+
current_ordering = (
199+
query_set.query.order_by
200+
or query_set.model._meta.ordering
201+
or []
202+
)
203+
query_set = query_set.order_by(*current_ordering, "pk")
189204
chunk_size = 2000 if query_set._prefetch_related_lookups else None
190205
return query_set.iterator(chunk_size=chunk_size)
191206

tests/serializers/test_natural.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
from unittest import mock
2+
13
from django.core import serializers
2-
from django.db import connection
4+
from django.db import connection, models
35
from django.test import TestCase
46

57
from .models import (
@@ -336,6 +338,20 @@ def nullable_natural_key_m2m_test(self, format):
336338
)
337339

338340

341+
def natural_key_m2m_totally_ordered_test(self, format):
342+
t1 = NaturalKeyThing.objects.create(key="t1")
343+
t2 = NaturalKeyThing.objects.create(key="t2")
344+
t3 = NaturalKeyThing.objects.create(key="t3")
345+
t1.other_things.add(t2, t3)
346+
347+
with mock.patch.object(models.QuerySet, "order_by") as mock_order_by:
348+
serializers.serialize(format, [t1], use_natural_foreign_keys=True)
349+
mock_order_by.assert_called_once_with("pk")
350+
mock_order_by.reset_mock()
351+
serializers.serialize(format, [t1], use_natural_foreign_keys=False)
352+
mock_order_by.assert_called_once_with("pk")
353+
354+
339355
# Dynamically register tests for each serializer
340356
register_tests(
341357
NaturalKeySerializerTests,
@@ -385,3 +401,8 @@ def nullable_natural_key_m2m_test(self, format):
385401
"test_%s_nullable_natural_key_m2m",
386402
nullable_natural_key_m2m_test,
387403
)
404+
register_tests(
405+
NaturalKeySerializerTests,
406+
"test_%s_natural_key_m2m_totally_ordered",
407+
natural_key_m2m_totally_ordered_test,
408+
)

0 commit comments

Comments
 (0)