mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
Fixed #35996 -- Fixed database serialization crash when serializing a many-to-many field that had a prefetch.
This commit is contained in:
parent
a8b70aeffd
commit
20f9f61805
@ -74,7 +74,11 @@ class Serializer(base.Serializer):
|
|||||||
return value.natural_key()
|
return value.natural_key()
|
||||||
|
|
||||||
def queryset_iterator(obj, field):
|
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:
|
else:
|
||||||
|
|
||||||
@ -82,12 +86,9 @@ class Serializer(base.Serializer):
|
|||||||
return self._value_from_field(value, value._meta.pk)
|
return self._value_from_field(value, value._meta.pk)
|
||||||
|
|
||||||
def queryset_iterator(obj, field):
|
def queryset_iterator(obj, field):
|
||||||
return (
|
query_set = getattr(obj, field.name).select_related(None).only("pk")
|
||||||
getattr(obj, field.name)
|
chunk_size = 2000 if query_set._prefetch_related_lookups else None
|
||||||
.select_related(None)
|
return query_set.iterator(chunk_size=chunk_size)
|
||||||
.only("pk")
|
|
||||||
.iterator()
|
|
||||||
)
|
|
||||||
|
|
||||||
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
|
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
|
||||||
field.name,
|
field.name,
|
||||||
|
@ -148,7 +148,11 @@ class Serializer(base.Serializer):
|
|||||||
self.xml.endElement("object")
|
self.xml.endElement("object")
|
||||||
|
|
||||||
def queryset_iterator(obj, field):
|
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:
|
else:
|
||||||
|
|
||||||
@ -156,12 +160,9 @@ class Serializer(base.Serializer):
|
|||||||
self.xml.addQuickElement("object", attrs={"pk": str(value.pk)})
|
self.xml.addQuickElement("object", attrs={"pk": str(value.pk)})
|
||||||
|
|
||||||
def queryset_iterator(obj, field):
|
def queryset_iterator(obj, field):
|
||||||
return (
|
query_set = getattr(obj, field.name).select_related(None).only("pk")
|
||||||
getattr(obj, field.name)
|
chunk_size = 2000 if query_set._prefetch_related_lookups else None
|
||||||
.select_related(None)
|
return query_set.iterator(chunk_size=chunk_size)
|
||||||
.only("pk")
|
|
||||||
.iterator()
|
|
||||||
)
|
|
||||||
|
|
||||||
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
|
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
|
||||||
field.name,
|
field.name,
|
||||||
|
@ -7,6 +7,7 @@ from django.core import serializers
|
|||||||
from django.core.serializers import SerializerDoesNotExist
|
from django.core.serializers import SerializerDoesNotExist
|
||||||
from django.core.serializers.base import ProgressBar
|
from django.core.serializers.base import ProgressBar
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
|
from django.db.models import Prefetch
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.test import SimpleTestCase, override_settings, skipUnlessDBFeature
|
from django.test import SimpleTestCase, override_settings, skipUnlessDBFeature
|
||||||
from django.test.utils import Approximate
|
from django.test.utils import Approximate
|
||||||
@ -18,6 +19,7 @@ from .models import (
|
|||||||
AuthorProfile,
|
AuthorProfile,
|
||||||
BaseModel,
|
BaseModel,
|
||||||
Category,
|
Category,
|
||||||
|
CategoryMetaData,
|
||||||
Child,
|
Child,
|
||||||
ComplexModel,
|
ComplexModel,
|
||||||
Movie,
|
Movie,
|
||||||
@ -275,18 +277,45 @@ class SerializersTestBase:
|
|||||||
serializers.serialize(self.serializer_name, [mv])
|
serializers.serialize(self.serializer_name, [mv])
|
||||||
|
|
||||||
def test_serialize_prefetch_related_m2m(self):
|
def test_serialize_prefetch_related_m2m(self):
|
||||||
# One query for the Article table and one for each prefetched m2m
|
# One query for the Article table, one for each prefetched m2m
|
||||||
# field.
|
# field, and one extra one for the nested prefetch for the Topics
|
||||||
with self.assertNumQueries(4):
|
# that have a relationship to the Category.
|
||||||
|
with self.assertNumQueries(5):
|
||||||
serializers.serialize(
|
serializers.serialize(
|
||||||
self.serializer_name,
|
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
|
# One query for the Article table, and three m2m queries for each
|
||||||
# article.
|
# article.
|
||||||
with self.assertNumQueries(7):
|
with self.assertNumQueries(7):
|
||||||
serializers.serialize(self.serializer_name, Article.objects.all())
|
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):
|
def test_serialize_with_null_pk(self):
|
||||||
"""
|
"""
|
||||||
Serialized data with no primary key results
|
Serialized data with no primary key results
|
||||||
|
Loading…
Reference in New Issue
Block a user