2
0
Эх сурвалжийг харах

Fixed #33937 -- Optimized serialization of related m2m fields without natural keys.

Mark Evans 2 жил өмнө
parent
commit
19e0587ee5

+ 1 - 0
AUTHORS

@@ -616,6 +616,7 @@ answer newbie questions, and generally made Django that much better:
     Mario Gonzalez <gonzalemario@gmail.com>
     Mariusz Felisiak <felisiak.mariusz@gmail.com>
     Mark Biggers <biggers@utsl.com>
+    Mark Evans <mark@meltdownlabs.com>
     Mark Gensler <mark.gensler@protonmail.com>
     mark@junklight.com
     Mark Lavin <markdlavin@gmail.com>

+ 7 - 1
django/core/serializers/python.py

@@ -70,14 +70,20 @@ class Serializer(base.Serializer):
                 def m2m_value(value):
                     return value.natural_key()
 
+                def queryset_iterator(obj, field):
+                    return getattr(obj, field.name).iterator()
+
             else:
 
                 def m2m_value(value):
                     return self._value_from_field(value, value._meta.pk)
 
+                def queryset_iterator(obj, field):
+                    return getattr(obj, field.name).only("pk").iterator()
+
             m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
                 field.name,
-                getattr(obj, field.name).iterator(),
+                queryset_iterator(obj, field),
             )
             self._current[field.name] = [m2m_value(related) for related in m2m_iter]
 

+ 7 - 1
django/core/serializers/xml_serializer.py

@@ -146,14 +146,20 @@ class Serializer(base.Serializer):
                         self.xml.endElement("natural")
                     self.xml.endElement("object")
 
+                def queryset_iterator(obj, field):
+                    return getattr(obj, field.name).iterator()
+
             else:
 
                 def handle_m2m(value):
                     self.xml.addQuickElement("object", attrs={"pk": str(value.pk)})
 
+                def queryset_iterator(obj, field):
+                    return getattr(obj, field.name).only("pk").iterator()
+
             m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
                 field.name,
-                getattr(obj, field.name).iterator(),
+                queryset_iterator(obj, field),
             )
             for relobj in m2m_iter:
                 handle_m2m(relobj)

+ 27 - 0
tests/serializers/tests.py

@@ -410,6 +410,33 @@ class SerializersTestBase:
         self.assertEqual(self._get_field_values(child_data, "parent_m2m"), [])
         self.assertEqual(self._get_field_values(child_data, "parent_data"), [])
 
+    def test_serialize_only_pk(self):
+        with self.assertNumQueries(5) as ctx:
+            serializers.serialize(
+                self.serializer_name,
+                Article.objects.all(),
+                use_natural_foreign_keys=False,
+            )
+
+        categories_sql = ctx[1]["sql"]
+        self.assertNotIn(connection.ops.quote_name("meta_data_id"), categories_sql)
+        meta_data_sql = ctx[2]["sql"]
+        self.assertNotIn(connection.ops.quote_name("kind"), meta_data_sql)
+
+    def test_serialize_no_only_pk_with_natural_keys(self):
+        with self.assertNumQueries(5) as ctx:
+            serializers.serialize(
+                self.serializer_name,
+                Article.objects.all(),
+                use_natural_foreign_keys=True,
+            )
+
+        categories_sql = ctx[1]["sql"]
+        self.assertNotIn(connection.ops.quote_name("meta_data_id"), categories_sql)
+        # CategoryMetaData has natural_key().
+        meta_data_sql = ctx[2]["sql"]
+        self.assertIn(connection.ops.quote_name("kind"), meta_data_sql)
+
 
 class SerializerAPITests(SimpleTestCase):
     def test_stream_class(self):