Opened 5 years ago

Last modified 4 years ago

#30796 closed Bug

Chaining select_related mutates original QuerySet. — at Version 1

Reported by: Darren Maki Owned by: nobody
Component: Database layer (models, ORM) Version: 2.2
Severity: Normal Keywords: select_related prefetch_related mutate queryset
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Darren Maki)

When creating a new QuerySet from an existing QuerySet that has had 'select_related' applied, if you apply another 'select_related' to the new QuerySet it will mutate the original QuerySet to also have the extra 'select_related'.

models.py

from django.db import models

class ModelA(models.Model):
    pass

class ModelB(models.Model):
    pass

class ModelC(models.Model):
    model_a = models.ForeignKey('foobar.ModelA', on_delete=models.CASCADE)
    model_b = models.ForeignKey('foobar.ModelB', on_delete=models.CASCADE)

test.py

query_1 = ModelC.objects.select_related('model_a')
print('QUERY 1:', str(query_1.query))

query_2 = query_1.select_related('model_b')
print('QUERY 2:', str(query_2.query))

print('QUERY 1:', str(query_1.query))

if str(query_1.query) == str(query_2.query):
    print('\n!!! The two queries are the same !!!\n')

output

QUERY 1: SELECT "foobar_modelc"."id", "foobar_modelc"."model_a_id", "foobar_modelc"."model_b_id", "foobar_modela"."id" FROM "foobar_modelc" INNER JOIN "foobar_modela" ON ("foobar_modelc"."model_a_id" = "foobar_modela"."id")
QUERY 2: SELECT "foobar_modelc"."id", "foobar_modelc"."model_a_id", "foobar_modelc"."model_b_id", "foobar_modela"."id", "foobar_modelb"."id" FROM "foobar_modelc" INNER JOIN "foobar_modela" ON ("foobar_modelc"."model_a_id" = "foobar_modela"."id") INNER JOIN "foobar_modelb" ON ("foobar_modelc"."model_b_id" = "foobar_modelb"."id")
QUERY 1: SELECT "foobar_modelc"."id", "foobar_modelc"."model_a_id", "foobar_modelc"."model_b_id", "foobar_modela"."id", "foobar_modelb"."id" FROM "foobar_modelc" INNER JOIN "foobar_modela" ON ("foobar_modelc"."model_a_id" = "foobar_modela"."id") INNER JOIN "foobar_modelb" ON ("foobar_modelc"."model_b_id" = "foobar_modelb"."id")

!!! The two queries are the same !!!

The expectation is that the original QuerySet is not mutated, and the two queries are different. This behavior also happens with 'prefetch_related'.

Since the QuerySet methods call 'self._clone()', and state that they return 'a new QuerySet instance', this behavior does not seem correct.

Change History (1)

comment:1 by Darren Maki, 5 years ago

Description: modified (diff)
Note: See TracTickets for help on using tickets.
Back to Top