From 183442864837386df1f24d9cd0b39a3671ef3b04 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Thu, 26 Jun 2008 01:01:21 +0000 Subject: [PATCH] Fixed a problem with values() and values_list() queries and nullable joins. Previously, if we were querying across a nullable join and then a non-nullable one, the second join would not be a LEFT OUTER join, which would exclude certain valid results from the result set. This is the same problem as [7597] but for values() field specifications, so this covers the second case where Django adds extra stuff to the select-clause. git-svn-id: http://code.djangoproject.com/svn/django/trunk@7740 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/models/sql/query.py | 8 +++++++- tests/regressiontests/queries/models.py | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 3044882a86..e6a3cd2679 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -679,12 +679,16 @@ class Query(object): for the join to contain NULL values on the left. If 'unconditional' is False, the join is only promoted if it is nullable, otherwise it is always promoted. + + Returns True if the join was promoted. """ if ((unconditional or self.alias_map[alias][NULLABLE]) and self.alias_map[alias] != self.LOUTER): data = list(self.alias_map[alias]) data[JOIN_TYPE] = self.LOUTER self.alias_map[alias] = tuple(data) + return True + return False def change_aliases(self, change_map): """ @@ -1294,10 +1298,12 @@ class Query(object): final_alias = join[LHS_ALIAS] col = join[LHS_JOIN_COL] joins = joins[:-1] + promote = False for join in joins[1:]: # Only nullable aliases are promoted, so we don't end up # doing unnecessary left outer joins here. - self.promote_alias(join) + if self.promote_alias(join, promote): + promote = True self.select.append((final_alias, col)) self.select_fields.append(field) except MultiJoin: diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 7270c21dac..5af48741d6 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -58,7 +58,7 @@ class Item(models.Model): class Report(models.Model): name = models.CharField(max_length=10) - creator = models.ForeignKey(Author, to_field='num') + creator = models.ForeignKey(Author, to_field='num', null=True) def __unicode__(self): return self.name @@ -191,6 +191,8 @@ by 'info'. Helps detect some problems later. >>> r1.save() >>> r2 = Report(name='r2', creator=a3) >>> r2.save() +>>> r3 = Report(name='r3') +>>> r3.save() Ordering by 'rank' gives us rank2, rank1, rank3. Ordering by the Meta.ordering will be rank3, rank2, rank1. @@ -713,5 +715,10 @@ in MySQL. This exercises that case. >>> mm = ManagedModel.objects.create(data='mm1', tag=t1, is_public=True) >>> ManagedModel.objects.update(data='mm') +A values() or values_list() query across joined models must use outer joins +appropriately. +>>> Report.objects.values_list("creator__extra__info", flat=True).order_by("name") +[u'e1', u'e2', None] + """}