From 161e26c2ec9f88bf0395941aaa2fd193b110affd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Thu, 22 Aug 2013 10:42:10 +0300 Subject: [PATCH] [1.6.x] Fixed #20955 -- select_related regression In cases where the same connection (from model A to model B along the same field) was needed multiple times in a select_related query, the join setup code mistakenly reused an existing join. Backpatch of 8d65b6082c8bf5df25608d8733470879a8a61d7d. Conflicts: django/db/models/sql/compiler.py tests/queries/tests.py --- django/db/models/sql/compiler.py | 5 ++--- tests/queries/models.py | 26 ++++++++++++++++++++++++++ tests/queries/tests.py | 22 +++++++++++++++++++++- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index f70750abed..e768cedf88 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -687,9 +687,8 @@ class SQLCompiler(object): # Use True here because we are looking at the _reverse_ side of # the relation, which is always nullable. new_nullable = True - table = model._meta.db_table - self.fill_related_selections(model._meta, table, cur_depth+1, - next, restricted, new_nullable) + self.fill_related_selections(model._meta, alias, cur_depth + 1, + next, restricted, new_nullable) def deferred_to_columns(self): """ diff --git a/tests/queries/models.py b/tests/queries/models.py index 71346d8be9..7248246261 100644 --- a/tests/queries/models.py +++ b/tests/queries/models.py @@ -497,3 +497,29 @@ class OrderItem(models.Model): def __str__(self): return '%s' % self.pk + +class BaseUser(models.Model): + pass + +@python_2_unicode_compatible +class Task(models.Model): + title = models.CharField(max_length=10) + owner = models.ForeignKey(BaseUser, related_name='owner') + creator = models.ForeignKey(BaseUser, related_name='creator') + + def __str__(self): + return self.title + +@python_2_unicode_compatible +class Staff(models.Model): + name = models.CharField(max_length=10) + + def __str__(self): + return self.name + +@python_2_unicode_compatible +class StaffUser(BaseUser): + staff = models.OneToOneField(Staff, related_name='user') + + def __str__(self): + return self.staff diff --git a/tests/queries/tests.py b/tests/queries/tests.py index ff6c0bb7a1..fbeadf726a 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -25,7 +25,7 @@ from .models import ( OneToOneCategory, NullableName, ProxyCategory, SingleObject, RelatedObject, ModelA, ModelB, ModelC, ModelD, Responsibility, Job, JobResponsibilities, BaseA, FK1, Identifier, Program, Channel, Page, Paragraph, Chapter, Book, - MyObject, Order, OrderItem) + MyObject, Order, OrderItem, Task, Staff, StaffUser) class BaseQuerysetTest(TestCase): @@ -2943,3 +2943,23 @@ class RelatedLookupTypeTests(TestCase): self.assertQuerysetEqual( ObjectB.objects.filter(objecta__in=[wrong_type]), [ob], lambda x: x) + +class Ticket20955Tests(TestCase): + def test_ticket_20955(self): + jack = Staff.objects.create(name='jackstaff') + jackstaff = StaffUser.objects.create(staff=jack) + jill = Staff.objects.create(name='jillstaff') + jillstaff = StaffUser.objects.create(staff=jill) + task = Task.objects.create(creator=jackstaff, owner=jillstaff, title="task") + task_get = Task.objects.get(pk=task.pk) + # Load data so that assertNumQueries doesn't complain about the get + # version's queries. + task_get.creator.staffuser.staff + task_get.owner.staffuser.staff + task_select_related = Task.objects.select_related( + 'creator__staffuser__staff', 'owner__staffuser__staff').get(pk=task.pk) + with self.assertNumQueries(0): + self.assertEqual(task_select_related.creator.staffuser.staff, + task_get.creator.staffuser.staff) + self.assertEqual(task_select_related.owner.staffuser.staff, + task_get.owner.staffuser.staff)