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
This commit is contained in:
Malcolm Tredinnick 2008-06-26 01:01:21 +00:00
parent b8f7b39ccc
commit 1834428648
2 changed files with 15 additions and 2 deletions

View File

@ -679,12 +679,16 @@ class Query(object):
for the join to contain NULL values on the left. If 'unconditional' is 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 False, the join is only promoted if it is nullable, otherwise it is
always promoted. always promoted.
Returns True if the join was promoted.
""" """
if ((unconditional or self.alias_map[alias][NULLABLE]) and if ((unconditional or self.alias_map[alias][NULLABLE]) and
self.alias_map[alias] != self.LOUTER): self.alias_map[alias] != self.LOUTER):
data = list(self.alias_map[alias]) data = list(self.alias_map[alias])
data[JOIN_TYPE] = self.LOUTER data[JOIN_TYPE] = self.LOUTER
self.alias_map[alias] = tuple(data) self.alias_map[alias] = tuple(data)
return True
return False
def change_aliases(self, change_map): def change_aliases(self, change_map):
""" """
@ -1294,10 +1298,12 @@ class Query(object):
final_alias = join[LHS_ALIAS] final_alias = join[LHS_ALIAS]
col = join[LHS_JOIN_COL] col = join[LHS_JOIN_COL]
joins = joins[:-1] joins = joins[:-1]
promote = False
for join in joins[1:]: for join in joins[1:]:
# Only nullable aliases are promoted, so we don't end up # Only nullable aliases are promoted, so we don't end up
# doing unnecessary left outer joins here. # 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.append((final_alias, col))
self.select_fields.append(field) self.select_fields.append(field)
except MultiJoin: except MultiJoin:

View File

@ -58,7 +58,7 @@ class Item(models.Model):
class Report(models.Model): class Report(models.Model):
name = models.CharField(max_length=10) 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): def __unicode__(self):
return self.name return self.name
@ -191,6 +191,8 @@ by 'info'. Helps detect some problems later.
>>> r1.save() >>> r1.save()
>>> r2 = Report(name='r2', creator=a3) >>> r2 = Report(name='r2', creator=a3)
>>> r2.save() >>> r2.save()
>>> r3 = Report(name='r3')
>>> r3.save()
Ordering by 'rank' gives us rank2, rank1, rank3. Ordering by the Meta.ordering Ordering by 'rank' gives us rank2, rank1, rank3. Ordering by the Meta.ordering
will be rank3, rank2, rank1. 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) >>> mm = ManagedModel.objects.create(data='mm1', tag=t1, is_public=True)
>>> ManagedModel.objects.update(data='mm') >>> 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]
"""} """}