1
0
mirror of https://github.com/django/django.git synced 2024-12-22 00:55:44 +00:00

Fixed #35996 -- Fixed database serialization crash when serializing a many-to-many field that had a prefetch.

This commit is contained in:
Erica Pisani 2024-12-18 09:03:02 +01:00 committed by Sarah Boyce
parent a8b70aeffd
commit 20f9f61805
3 changed files with 49 additions and 18 deletions

View File

@ -74,7 +74,11 @@ class Serializer(base.Serializer):
return value.natural_key()
def queryset_iterator(obj, field):
return getattr(obj, field.name).iterator()
attr = getattr(obj, field.name)
chunk_size = (
2000 if getattr(attr, "prefetch_cache_name", None) else None
)
return attr.iterator(chunk_size)
else:
@ -82,12 +86,9 @@ class Serializer(base.Serializer):
return self._value_from_field(value, value._meta.pk)
def queryset_iterator(obj, field):
return (
getattr(obj, field.name)
.select_related(None)
.only("pk")
.iterator()
)
query_set = getattr(obj, field.name).select_related(None).only("pk")
chunk_size = 2000 if query_set._prefetch_related_lookups else None
return query_set.iterator(chunk_size=chunk_size)
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
field.name,

View File

@ -148,7 +148,11 @@ class Serializer(base.Serializer):
self.xml.endElement("object")
def queryset_iterator(obj, field):
return getattr(obj, field.name).iterator()
attr = getattr(obj, field.name)
chunk_size = (
2000 if getattr(attr, "prefetch_cache_name", None) else None
)
return attr.iterator(chunk_size)
else:
@ -156,12 +160,9 @@ class Serializer(base.Serializer):
self.xml.addQuickElement("object", attrs={"pk": str(value.pk)})
def queryset_iterator(obj, field):
return (
getattr(obj, field.name)
.select_related(None)
.only("pk")
.iterator()
)
query_set = getattr(obj, field.name).select_related(None).only("pk")
chunk_size = 2000 if query_set._prefetch_related_lookups else None
return query_set.iterator(chunk_size=chunk_size)
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
field.name,

View File

@ -7,6 +7,7 @@ from django.core import serializers
from django.core.serializers import SerializerDoesNotExist
from django.core.serializers.base import ProgressBar
from django.db import connection, transaction
from django.db.models import Prefetch
from django.http import HttpResponse
from django.test import SimpleTestCase, override_settings, skipUnlessDBFeature
from django.test.utils import Approximate
@ -18,6 +19,7 @@ from .models import (
AuthorProfile,
BaseModel,
Category,
CategoryMetaData,
Child,
ComplexModel,
Movie,
@ -275,18 +277,45 @@ class SerializersTestBase:
serializers.serialize(self.serializer_name, [mv])
def test_serialize_prefetch_related_m2m(self):
# One query for the Article table and one for each prefetched m2m
# field.
with self.assertNumQueries(4):
# One query for the Article table, one for each prefetched m2m
# field, and one extra one for the nested prefetch for the Topics
# that have a relationship to the Category.
with self.assertNumQueries(5):
serializers.serialize(
self.serializer_name,
Article.objects.prefetch_related("categories", "meta_data", "topics"),
Article.objects.prefetch_related(
"meta_data",
"topics",
Prefetch(
"categories",
queryset=Category.objects.prefetch_related("topic_set"),
),
),
)
# One query for the Article table, and three m2m queries for each
# article.
with self.assertNumQueries(7):
serializers.serialize(self.serializer_name, Article.objects.all())
def test_serialize_prefetch_related_m2m_with_natural_keys(self):
# One query for the Article table, one for each prefetched m2m
# field, and a query to get the categories for each Article (two in
# total).
with self.assertNumQueries(5):
serializers.serialize(
self.serializer_name,
Article.objects.prefetch_related(
Prefetch(
"meta_data",
queryset=CategoryMetaData.objects.prefetch_related(
"category_set"
),
),
"topics",
),
use_natural_foreign_keys=True,
)
def test_serialize_with_null_pk(self):
"""
Serialized data with no primary key results