Ticket #18375: ticket_18375_15.diff

File ticket_18375_15.diff, 4.3 KB (added by Anssi Kääriäinen, 11 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..a808183 100644
    a b class SQLEvaluator(object):  
    99        self.cols = []
    1010
    1111        self.contains_aggregate = False
     12        self.used_joins = []
    1213        self.expression.prepare(self, query, allow_joins)
    1314
    1415    def prepare(self):
    class SQLEvaluator(object):  
    5152                field, source, opts, join_list, last, _ = query.setup_joins(
    5253                    field_list, query.get_meta(),
    5354                    query.get_initial_alias(), False)
     55                # Note that even if we trim some joins away, the joins are
     56                # still usable by this node.
     57                self.used_joins.extend(join_list)
    5458                col, _, join_list = query.trim_joins(source, join_list, last, False)
    55 
    5659                self.cols.append((node, (join_list[-1], col)))
    5760            except FieldDoesNotExist:
    5861                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 f5a477f..cf4f7f5 100644
    a b class Query(object):  
    11001100        elif isinstance(value, ExpressionNode):
    11011101            # If value is a query expression, evaluate it
    11021102            value = SQLEvaluator(value, self)
     1103            if value.used_joins and can_reuse is not None:
     1104                can_reuse.update(value.used_joins)
     1105
    11031106            having_clause = value.contains_aggregate
    11041107
    11051108        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 8b7af2c..d3cbe23 100644
    a b Miscellaneous  
    563563  :ref:`Q() expressions <complex-lookups-with-q>` and ``QuerySet`` combining where
    564564  the operators are used as boolean AND and OR operators.
    565565
     566* When :ref:`F() expressions <query-expressions>` were used in lookups spanning
     567  multi-valued relations it was possible that the F() and other lookups in a
     568  single filter() call didn't target the same relation. This was change and now
     569  F() expressions will reuse the same relation.
     570
    566571* The :ttag:`csrf_token` template tag is no longer enclosed in a div. If you need
    567572  HTML validation against pre-HTML5 Strict DTDs, you should add a div around it
    568573  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):  
    219219        )
    220220        acme.num_employees = F("num_employees") + 16
    221221        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)
Back to Top