#34779 closed Bug (fixed)
Serializer makes unnecessary joins
Reported by: | Juan Alvarez | Owned by: | Juan Alvarez |
---|---|---|---|
Component: | Core (Serialization) | Version: | 4.2 |
Severity: | Release blocker | Keywords: | |
Cc: | Martin Svoboda | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
When handling M2M fields, the serializer would make unnecessary joins, as it has an empty select_related
. Its default behavior is to select all related fields. This can even trigger a recursive addition of related fields, that end up in an OperationalError
because of too many joins.
The regression seems to have been introduced in https://code.djangoproject.com/ticket/34620
Change History (9)
comment:1 by , 15 months ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:2 by , 15 months ago
Has patch: | set |
---|---|
Type: | Uncategorized → Bug |
follow-up: 4 comment:3 by , 15 months ago
Resolution: | → needsinfo |
---|---|
Status: | assigned → closed |
comment:4 by , 15 months ago
Replying to Natalia Bidart:
Hello Juan, thanks for your report.
Do you think you could provide a reproducer? A small django test project, or a set of models exercising the issue, or a failing test case. That way we can triage the issue more accurately.
Natalia.
Natalia,
For reference, I did submit a potential fix at https://github.com/django/django/pull/17164
Actually, the existing test did _kind_ of covered the regression. The issue was that since the model had null=True
, the empty select_related()
did not use the field, because of:
def select_related_descend(field, restricted, requested, select_mask, reverse=False): # [...] if not restricted and field.null: return False
So this change:
-
tests/serializers/models/base.py
diff --git a/tests/serializers/models/base.py b/tests/serializers/models/base.py index e9f548ad3c..c5a4a0f580 100644
a b class TopicManager(models.Manager): 60 60 61 61 class Topic(models.Model): 62 62 name = models.CharField(max_length=255) 63 category = models.ForeignKey(Category, models.CASCADE , null=True)63 category = models.ForeignKey(Category, models.CASCADE) 64 64 objects = TopicManager()
Makes this test fail:
def test_serialize_only_pk(self): with self.assertNumQueries(7) 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) topics_data_sql = ctx[3]["sql"] self.assertNotIn(connection.ops.quote_name("category_id"), topics_data_sql)
because there is a join to category_id
when Topic.category
is not nullable.
If you consider this not sufficient, I can try to come up with a more complete example. But this issue is breaking our application, and the proposed fix does fix it.
comment:5 by , 15 months ago
Resolution: | needsinfo |
---|---|
Status: | closed → new |
comment:6 by , 15 months ago
Cc: | added |
---|---|
Component: | Uncategorized → Core (Serialization) |
Severity: | Normal → Release blocker |
Status: | new → assigned |
Triage Stage: | Unreviewed → Accepted |
Good catch! Thanks for the report and patch.
comment:7 by , 15 months ago
Triage Stage: | Accepted → Ready for checkin |
---|
Hello Juan, thanks for your report.
Do you think you could provide a reproducer? A small django test project, or a set of models exercising the issue, or a failing test case. That way we can triage the issue more accurately.
Natalia.