From 3c6318e831658b88ba7c3e04f315329071b25e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Thu, 21 Feb 2013 11:12:08 +0200 Subject: [PATCH] Fixed #19870 -- Regression in select_related in inheritance cases There was a regression in case two models inherited the same parent, and one contained a foreign key to other. When select_related travelled the foreign key the other model reused the parent join made by the first model. This was likely caused by Query.join_parent_model() addition in commit 68985db48212c701a3d975636123a5d79bdc006f. Thanks to Trac alias loic84 for report & tests. --- django/db/models/sql/compiler.py | 13 +++++++++---- .../select_related_regress/models.py | 14 ++++++++++++++ .../select_related_regress/tests.py | 13 ++++++++++++- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 977eb1afa2..22d025ecb2 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -265,14 +265,19 @@ class SQLCompiler(object): qn2 = self.connection.ops.quote_name aliases = set() only_load = self.deferred_to_columns() - seen = self.query.included_inherited_models.copy() - if start_alias: - seen[None] = start_alias + if not start_alias: + start_alias = self.query.get_initial_alias() + # The 'seen_models' is used to optimize checking the needed parent + # alias for a given field. This also includes None -> start_alias to + # be used by local fields. + seen_models = {None: start_alias} + for field, model in opts.get_fields_with_model(): if from_parent and model is not None and issubclass(from_parent, model): # Avoid loading data for already loaded parents. continue - alias = self.query.join_parent_model(opts, model, start_alias, seen) + alias = self.query.join_parent_model(opts, model, start_alias, + seen_models) table = self.query.alias_map[alias].table_name if table in only_load and field.column not in only_load[table]: continue diff --git a/tests/regressiontests/select_related_regress/models.py b/tests/regressiontests/select_related_regress/models.py index a291a547f7..620b8eb5ea 100644 --- a/tests/regressiontests/select_related_regress/models.py +++ b/tests/regressiontests/select_related_regress/models.py @@ -94,3 +94,17 @@ class Item(models.Model): def __str__(self): return self.name + +# Models for testing bug #19870. +@python_2_unicode_compatible +class Fowl(models.Model): + name = models.CharField(max_length=10) + + def __str__(self): + return self.name + +class Hen(Fowl): + pass + +class Chick(Fowl): + mother = models.ForeignKey(Hen) diff --git a/tests/regressiontests/select_related_regress/tests.py b/tests/regressiontests/select_related_regress/tests.py index 0e27d07c4d..f6d21b2dd9 100644 --- a/tests/regressiontests/select_related_regress/tests.py +++ b/tests/regressiontests/select_related_regress/tests.py @@ -5,7 +5,7 @@ from django.utils import six from .models import (Building, Child, Device, Port, Item, Country, Connection, ClientStatus, State, Client, SpecialClient, TUser, Person, Student, - Organizer, Class, Enrollment) + Organizer, Class, Enrollment, Hen, Chick) class SelectRelatedRegressTests(TestCase): @@ -162,3 +162,14 @@ class SelectRelatedRegressTests(TestCase): # The select_related join was promoted as there is already an # existing join. self.assertTrue('LEFT OUTER' in str(qs.query)) + + def test_regression_19870(self): + """ + Regression for #19870 + + """ + hen = Hen.objects.create(name='Hen') + chick = Chick.objects.create(name='Chick', mother=hen) + + self.assertEqual(Chick.objects.all()[0].mother.name, 'Hen') + self.assertEqual(Chick.objects.select_related()[0].mother.name, 'Hen')