Fixed #17424 -- annotate() + exclude() bug

The bug was already fixed by 01b9c3d519,
so only tests added.

At the same time promote_joins()'s uncoditional flag is gone, it isn't
needed for anything any more.
This commit is contained in:
Anssi Kääriäinen 2013-08-07 12:38:30 +03:00
parent d53e574676
commit c7739e30b2
2 changed files with 28 additions and 5 deletions

View File

@ -480,7 +480,7 @@ class Query(object):
rhs_used_joins = set(change_map.values()) rhs_used_joins = set(change_map.values())
to_promote = [alias for alias in self.tables to_promote = [alias for alias in self.tables
if alias not in rhs_used_joins] if alias not in rhs_used_joins]
self.promote_joins(to_promote, True) self.promote_joins(to_promote)
# Now relabel a copy of the rhs where-clause and add it to the current # Now relabel a copy of the rhs where-clause and add it to the current
# one. # one.
@ -659,7 +659,7 @@ class Query(object):
""" Decreases the reference count for this alias. """ """ Decreases the reference count for this alias. """
self.alias_refcount[alias] -= amount self.alias_refcount[alias] -= amount
def promote_joins(self, aliases, unconditional=False): def promote_joins(self, aliases):
""" """
Promotes recursively the join type of given aliases and its children to Promotes recursively the join type of given aliases and its children to
an outer join. If 'unconditional' is False, the join is only promoted if an outer join. If 'unconditional' is False, the join is only promoted if
@ -682,12 +682,14 @@ class Query(object):
# isn't really joined at all in the query, so we should not # isn't really joined at all in the query, so we should not
# alter its join type. # alter its join type.
continue continue
# Only the first alias (skipped above) should have None join_type
assert self.alias_map[alias].join_type is not None
parent_alias = self.alias_map[alias].lhs_alias parent_alias = self.alias_map[alias].lhs_alias
parent_louter = (parent_alias parent_louter = (parent_alias
and self.alias_map[parent_alias].join_type == self.LOUTER) and self.alias_map[parent_alias].join_type == self.LOUTER)
already_louter = self.alias_map[alias].join_type == self.LOUTER already_louter = self.alias_map[alias].join_type == self.LOUTER
if ((unconditional or self.alias_map[alias].nullable if ((self.alias_map[alias].nullable or parent_louter) and
or parent_louter) and not already_louter): not already_louter):
data = self.alias_map[alias]._replace(join_type=self.LOUTER) data = self.alias_map[alias]._replace(join_type=self.LOUTER)
self.alias_map[alias] = data self.alias_map[alias] = data
# Join type of 'alias' changed, so re-examine all aliases that # Join type of 'alias' changed, so re-examine all aliases that
@ -986,7 +988,7 @@ class Query(object):
# If the aggregate references a model or field that requires a join, # If the aggregate references a model or field that requires a join,
# those joins must be LEFT OUTER - empty join rows must be returned # those joins must be LEFT OUTER - empty join rows must be returned
# in order for zeros to be returned for those aggregates. # in order for zeros to be returned for those aggregates.
self.promote_joins(join_list, True) self.promote_joins(join_list)
col = targets[0].column col = targets[0].column
source = sources[0] source = sources[0]

View File

@ -596,3 +596,24 @@ class BaseAggregateTestCase(TestCase):
self.assertEqual( self.assertEqual(
max_books_per_rating, max_books_per_rating,
{'books_per_rating__max': 3}) {'books_per_rating__max': 3})
def test_ticket17424(self):
"""
Check that doing exclude() on a foreign model after annotate()
doesn't crash.
"""
all_books = list(Book.objects.values_list('pk', flat=True).order_by('pk'))
annotated_books = Book.objects.order_by('pk').annotate(one=Count("id"))
# The value doesn't matter, we just need any negative
# constraint on a related model that's a noop.
excluded_books = annotated_books.exclude(publisher__name="__UNLIKELY_VALUE__")
# Try to generate query tree
str(excluded_books.query)
self.assertQuerysetEqual(excluded_books, all_books, lambda x: x.pk)
# Check internal state
self.assertIsNone(annotated_books.query.alias_map["aggregation_book"].join_type)
self.assertIsNone(excluded_books.query.alias_map["aggregation_book"].join_type)