mirror of
https://github.com/django/django.git
synced 2025-01-03 06:55:47 +00:00
Fixed #34620 -- Fixed serialization crash on m2m fields without natural keys when base querysets use select_related().
Regression in 19e0587ee5
.
Thanks Martin Svoboda for the report.
This commit is contained in:
parent
1136aa5005
commit
f9936deed1
@ -79,7 +79,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 getattr(obj, field.name).only("pk").iterator()
|
return (
|
||||||
|
getattr(obj, field.name).select_related().only("pk").iterator()
|
||||||
|
)
|
||||||
|
|
||||||
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
|
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
|
||||||
field.name,
|
field.name,
|
||||||
|
@ -155,7 +155,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 getattr(obj, field.name).only("pk").iterator()
|
return (
|
||||||
|
getattr(obj, field.name).select_related().only("pk").iterator()
|
||||||
|
)
|
||||||
|
|
||||||
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
|
m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
|
||||||
field.name,
|
field.name,
|
||||||
|
@ -43,3 +43,7 @@ Bugfixes
|
|||||||
* Fixed a regression in Django 4.2 that caused a crash of querysets on SQLite
|
* Fixed a regression in Django 4.2 that caused a crash of querysets on SQLite
|
||||||
when filtering on ``DecimalField`` against values outside of the defined
|
when filtering on ``DecimalField`` against values outside of the defined
|
||||||
range (:ticket:`34590`).
|
range (:ticket:`34590`).
|
||||||
|
|
||||||
|
* Fixed a regression in Django 4.2 that caused a serialization crash on a
|
||||||
|
``ManyToManyField`` without a natural key when its ``Manager``’s base
|
||||||
|
``QuerySet`` used ``select_related()`` (:ticket:`34620`).
|
||||||
|
@ -53,12 +53,24 @@ class Author(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class TopicManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
return super().get_queryset().select_related("category")
|
||||||
|
|
||||||
|
|
||||||
|
class Topic(models.Model):
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
category = models.ForeignKey(Category, models.CASCADE, null=True)
|
||||||
|
objects = TopicManager()
|
||||||
|
|
||||||
|
|
||||||
class Article(models.Model):
|
class Article(models.Model):
|
||||||
author = models.ForeignKey(Author, models.CASCADE)
|
author = models.ForeignKey(Author, models.CASCADE)
|
||||||
headline = models.CharField(max_length=50)
|
headline = models.CharField(max_length=50)
|
||||||
pub_date = models.DateTimeField()
|
pub_date = models.DateTimeField()
|
||||||
categories = models.ManyToManyField(Category)
|
categories = models.ManyToManyField(Category)
|
||||||
meta_data = models.ManyToManyField(CategoryMetaData)
|
meta_data = models.ManyToManyField(CategoryMetaData)
|
||||||
|
topics = models.ManyToManyField(Topic)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ("pub_date",)
|
ordering = ("pub_date",)
|
||||||
|
@ -38,7 +38,8 @@ class JsonSerializerTestCase(SerializersTestBase, TestCase):
|
|||||||
%(first_category_pk)s,
|
%(first_category_pk)s,
|
||||||
%(second_category_pk)s
|
%(second_category_pk)s
|
||||||
],
|
],
|
||||||
"meta_data": []
|
"meta_data": [],
|
||||||
|
"topics": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -27,7 +27,8 @@ class JsonlSerializerTestCase(SerializersTestBase, TestCase):
|
|||||||
'"headline": "Poker has no place on ESPN",'
|
'"headline": "Poker has no place on ESPN",'
|
||||||
'"pub_date": "2006-06-16T11:00:00",'
|
'"pub_date": "2006-06-16T11:00:00",'
|
||||||
'"categories": [%(first_category_pk)s,%(second_category_pk)s],'
|
'"categories": [%(first_category_pk)s,%(second_category_pk)s],'
|
||||||
'"meta_data": []}}\n'
|
'"meta_data": [],'
|
||||||
|
'"topics": []}}\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -26,6 +26,7 @@ class XmlSerializerTestCase(SerializersTestBase, TestCase):
|
|||||||
<field name="pub_date" type="DateTimeField">2006-06-16T11:00:00</field>
|
<field name="pub_date" type="DateTimeField">2006-06-16T11:00:00</field>
|
||||||
<field name="categories" rel="ManyToManyRel" to="serializers.category"><object pk="%(first_category_pk)s"></object><object pk="%(second_category_pk)s"></object></field>
|
<field name="categories" rel="ManyToManyRel" to="serializers.category"><object pk="%(first_category_pk)s"></object><object pk="%(second_category_pk)s"></object></field>
|
||||||
<field name="meta_data" rel="ManyToManyRel" to="serializers.categorymetadata"></field>
|
<field name="meta_data" rel="ManyToManyRel" to="serializers.categorymetadata"></field>
|
||||||
|
<field name="topics" rel="ManyToManyRel" to="serializers.topic"></field>
|
||||||
</object>
|
</object>
|
||||||
</django-objects>""" # NOQA
|
</django-objects>""" # NOQA
|
||||||
|
|
||||||
|
@ -113,6 +113,7 @@ class YamlSerializerTestCase(SerializersTestBase, TestCase):
|
|||||||
)
|
)
|
||||||
+ """
|
+ """
|
||||||
meta_data: []
|
meta_data: []
|
||||||
|
topics: []
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -277,14 +277,14 @@ class SerializersTestBase:
|
|||||||
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 and one for each prefetched m2m
|
||||||
# field.
|
# field.
|
||||||
with self.assertNumQueries(3):
|
with self.assertNumQueries(4):
|
||||||
serializers.serialize(
|
serializers.serialize(
|
||||||
self.serializer_name,
|
self.serializer_name,
|
||||||
Article.objects.prefetch_related("categories", "meta_data"),
|
Article.objects.prefetch_related("categories", "meta_data", "topics"),
|
||||||
)
|
)
|
||||||
# One query for the Article table, and two m2m queries for each
|
# One query for the Article table, and three m2m queries for each
|
||||||
# article.
|
# article.
|
||||||
with self.assertNumQueries(5):
|
with self.assertNumQueries(7):
|
||||||
serializers.serialize(self.serializer_name, Article.objects.all())
|
serializers.serialize(self.serializer_name, Article.objects.all())
|
||||||
|
|
||||||
def test_serialize_with_null_pk(self):
|
def test_serialize_with_null_pk(self):
|
||||||
@ -409,7 +409,7 @@ class SerializersTestBase:
|
|||||||
self.assertEqual(self._get_field_values(child_data, "parent_data"), [])
|
self.assertEqual(self._get_field_values(child_data, "parent_data"), [])
|
||||||
|
|
||||||
def test_serialize_only_pk(self):
|
def test_serialize_only_pk(self):
|
||||||
with self.assertNumQueries(5) as ctx:
|
with self.assertNumQueries(7) as ctx:
|
||||||
serializers.serialize(
|
serializers.serialize(
|
||||||
self.serializer_name,
|
self.serializer_name,
|
||||||
Article.objects.all(),
|
Article.objects.all(),
|
||||||
@ -420,9 +420,11 @@ class SerializersTestBase:
|
|||||||
self.assertNotIn(connection.ops.quote_name("meta_data_id"), categories_sql)
|
self.assertNotIn(connection.ops.quote_name("meta_data_id"), categories_sql)
|
||||||
meta_data_sql = ctx[2]["sql"]
|
meta_data_sql = ctx[2]["sql"]
|
||||||
self.assertNotIn(connection.ops.quote_name("kind"), meta_data_sql)
|
self.assertNotIn(connection.ops.quote_name("kind"), meta_data_sql)
|
||||||
|
topics_data_sql = ctx[3]["sql"]
|
||||||
|
self.assertNotIn(connection.ops.quote_name("category_id"), topics_data_sql)
|
||||||
|
|
||||||
def test_serialize_no_only_pk_with_natural_keys(self):
|
def test_serialize_no_only_pk_with_natural_keys(self):
|
||||||
with self.assertNumQueries(5) as ctx:
|
with self.assertNumQueries(7) as ctx:
|
||||||
serializers.serialize(
|
serializers.serialize(
|
||||||
self.serializer_name,
|
self.serializer_name,
|
||||||
Article.objects.all(),
|
Article.objects.all(),
|
||||||
@ -434,6 +436,8 @@ class SerializersTestBase:
|
|||||||
# CategoryMetaData has natural_key().
|
# CategoryMetaData has natural_key().
|
||||||
meta_data_sql = ctx[2]["sql"]
|
meta_data_sql = ctx[2]["sql"]
|
||||||
self.assertIn(connection.ops.quote_name("kind"), meta_data_sql)
|
self.assertIn(connection.ops.quote_name("kind"), meta_data_sql)
|
||||||
|
topics_data_sql = ctx[3]["sql"]
|
||||||
|
self.assertNotIn(connection.ops.quote_name("category_id"), topics_data_sql)
|
||||||
|
|
||||||
|
|
||||||
class SerializerAPITests(SimpleTestCase):
|
class SerializerAPITests(SimpleTestCase):
|
||||||
|
Loading…
Reference in New Issue
Block a user