Ticket #18375: ticket_18375_master.diff
File ticket_18375_master.diff, 4.7 KB (added by , 12 years ago) |
---|
-
django/db/models/sql/expressions.py
diff --git a/django/db/models/sql/expressions.py b/django/db/models/sql/expressions.py index 3745099..cf38a17 100644
a b 1 1 from django.core.exceptions import FieldError 2 2 from django.db.models.constants import LOOKUP_SEP 3 3 from django.db.models.fields import FieldDoesNotExist 4 from django.db.models.sql.constants import REUSE_ALL 4 5 5 6 class SQLEvaluator(object): 6 7 def __init__(self, expression, query, allow_joins=True): … … class SQLEvaluator(object): 9 10 self.cols = [] 10 11 11 12 self.contains_aggregate = False 13 self.used_joins = [] 12 14 self.expression.prepare(self, query, allow_joins) 13 15 14 16 def prepare(self): … … class SQLEvaluator(object): 50 52 try: 51 53 field, source, opts, join_list, last, _ = query.setup_joins( 52 54 field_list, query.get_meta(), 53 query.get_initial_alias(), False) 55 query.get_initial_alias(), REUSE_ALL) 56 # Note that even if we trim some joins away, the joins are 57 # still usable by this node. 58 self.used_joins.extend(join_list) 54 59 col, _, join_list = query.trim_joins(source, join_list, last, False) 55 56 60 self.cols.append((node, (join_list[-1], col))) 57 61 except FieldDoesNotExist: 58 62 raise FieldError("Cannot resolve keyword %r into field. " -
django/db/models/sql/query.py
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 4cfb816..f857137 100644
a b class Query(object): 1097 1097 elif isinstance(value, ExpressionNode): 1098 1098 # If value is a query expression, evaluate it 1099 1099 value = SQLEvaluator(value, self) 1100 if value.used_joins and can_reuse is not None: 1101 can_reuse.update(value.used_joins) 1100 1102 having_clause = value.contains_aggregate 1101 1103 1102 1104 for alias, aggregate in self.aggregates.items(): -
docs/releases/1.5.txt
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index b73bb04..5bd4e8b 100644
a b Miscellaneous 575 575 :ref:`Q() expressions <complex-lookups-with-q>` and ``QuerySet`` combining where 576 576 the operators are used as boolean AND and OR operators. 577 577 578 * When :ref:`F() expressions <query-expressions>` were used in lookups spanning 579 multi-valued relations it was possible that the F() and other lookups in a 580 single filter() call didn't target the same relation. This was change and now 581 F() expressions will reuse the same relation. 582 578 583 * The :ttag:`csrf_token` template tag is no longer enclosed in a div. If you need 579 584 HTML validation against pre-HTML5 Strict DTDs, you should add a div around it 580 585 in your pages. -
tests/modeltests/expressions/tests.py
diff --git a/tests/modeltests/expressions/tests.py b/tests/modeltests/expressions/tests.py index 99eb07e..ba3f827 100644
a b class ExpressionsTests(TestCase): 219 219 ) 220 220 acme.num_employees = F("num_employees") + 16 221 221 self.assertRaises(TypeError, acme.save) 222 223 def test_ticket_18375_join_reuse(self): 224 # Test that reverse multijoin F() references and the lookup target 225 # the same join. Pre #18375 the F() join was generated first, and the 226 # lookup couldn't reuse that join. 227 qs = Employee.objects.filter( 228 company_ceo_set__num_chairs=F('company_ceo_set__num_employees')) 229 self.assertEqual(str(qs.query).count('JOIN'), 1) 230 231 def test_ticket_18375_kwarg_ordering(self): 232 # The next query was dict-randomization dependent - if the "gte=1" 233 # was seen first, then the F() will reuse the join generated by the 234 # gte lookup, if F() was seen first, then it generated a join the 235 # other lookups could not reuse. 236 qs = Employee.objects.filter( 237 company_ceo_set__num_chairs=F('company_ceo_set__num_employees'), 238 company_ceo_set__num_chairs__gte=1) 239 self.assertEqual(str(qs.query).count('JOIN'), 1) 240 241 def test_ticket_18375_kwarg_ordering_2(self): 242 # Another similar case for F() than above. Now we have the same join 243 # in two filter kwargs, one in the lhs lookup, one in F. Here pre 244 # #18375 the amount of joins generated was random if dict 245 # randomization was enabled, that is the generated query dependend 246 # on which clause was seen first. 247 qs = Employee.objects.filter( 248 company_ceo_set__num_employees=F('pk'), 249 pk=F('company_ceo_set__num_employees') 250 ) 251 self.assertEqual(str(qs.query).count('JOIN'), 1)