Ticket #10790: 10790v4.diff

File 10790v4.diff, 9.3 KB (added by Philippe Raoult, 13 years ago)

updated for current trunk

  • tests/modeltests/null_trimjoin/__init__.py

     
     1# dummy text for patch
  • tests/modeltests/null_trimjoin/tests.py

     
     1from django.test import TestCase
     2from models import Article, Reporter
     3
     4class OneToOneTests(TestCase):
     5
     6    def setUp(self):
     7        self.r = Reporter(name='John Smith')
     8        self.r.save()
     9        self.a = Article(headline="First", reporter=self.r)
     10        self.a.save()
     11        self.a2 = Article(headline="Second")
     12        self.a2.save()
     13
     14    def test_query_with_isnull(self):
     15        """Querying with isnull should not join Reporter table."""
     16        q = Article.objects.filter(reporter=None)
     17        # check that reporter is not in the query's used_aliases
     18        self.assertFalse('null_trimjoin_reporter' in q.query.used_aliases)
     19        self.assertTrue('null_trimjoin_article' in q.query.used_aliases)
     20        # but it should still be in query.tables
     21        self.assertTrue('null_trimjoin_article' in q.query.tables)
     22
     23    def test_query_across_tables(self):
     24        """Querying across several tables should strip only the last join, while
     25        preserving the preceding left outer joins."""
     26        q = Article.objects.filter(reporter__type=None)
     27        self.assertEquals(len(q), 2)
     28        self.assertTrue('null_trimjoin_article' in q.query.used_aliases)
     29        self.assertTrue('null_trimjoin_reporter' in q.query.used_aliases)
     30        self.assertFalse('null_trimjoin_reportertype' in q.query.used_aliases)
     31
     32    def test_m2m_query(self):
     33        """Querying across m2m field should not strip the m2m table from join."""
     34        q = Article.objects.filter(reporter__category__isnull=True)
     35        self.assertTrue('null_trimjoin_article' in q.query.used_aliases)
     36        self.assertTrue('null_trimjoin_reporter' in q.query.used_aliases)
     37        self.assertTrue('null_trimjoin_category' in q.query.used_aliases)
     38
     39    def test_reverse_query(self):
     40        """Reverse querying with isnull should not strip the join."""
     41        q = Reporter.objects.filter(article__isnull=True)
     42        self.assertTrue('null_trimjoin_reporter' in q.query.used_aliases)
     43
     44
  • tests/modeltests/null_trimjoin/models.py

     
     1"""
     2Do not join table when querying on isnull
     3
     4"""
     5
     6from django.db import models
     7
     8class Category(models.Model):
     9    name = models.CharField(max_length=30)
     10
     11class ReporterType(models.Model):
     12    name = models.CharField(max_length=30)
     13
     14class Reporter(models.Model):
     15    name = models.CharField(max_length=30)
     16    type = models.ForeignKey(ReporterType, null=True)
     17    category = models.ManyToManyField(Category, null=True)
     18
     19    def __unicode__(self):
     20        return self.name
     21
     22class Article(models.Model):
     23    headline = models.CharField(max_length=100)
     24    reporter = models.ForeignKey(Reporter, null=True)
     25
     26    class Meta:
     27        ordering = ('headline',)
     28
     29    def __unicode__(self):
     30        return self.headline
     31
  • django/db/models/sql/compiler.py

     
    384384        pieces = name.split(LOOKUP_SEP)
    385385        if not alias:
    386386            alias = self.query.get_initial_alias()
    387         field, target, opts, joins, last, extra = self.query.setup_joins(pieces,
    388                 opts, alias, False)
     387        field, target, opts, joins, last, extra, allow_trim_join = self.query.setup_joins(
     388                pieces, opts, alias, False)
    389389        alias = joins[-1]
    390390        col = target.column
    391391        if not field.rel:
  • django/db/models/sql/expressions.py

     
    4444            self.cols[node] = query.aggregate_select[node.name]
    4545        else:
    4646            try:
    47                 field, source, opts, join_list, last, _ = query.setup_joins(
     47                field, source, opts, join_list, last, _, allow_trim_join = query.setup_joins(
    4848                    field_list, query.get_meta(),
    4949                    query.get_initial_alias(), False)
    5050                col, _, join_list = query.trim_joins(source, join_list, last, False)
  • django/db/models/sql/query.py

     
    694694            return True
    695695        return False
    696696
     697    def demote_alias(self, alias):
     698        """
     699        Demotes the join type of an alias to an inner join.
     700        """
     701        data = list(self.alias_map[alias])
     702        data[JOIN_TYPE] = self.INNER
     703        self.alias_map[alias] = tuple(data)
     704
    697705    def promote_alias_chain(self, chain, must_promote=False):
    698706        """
    699707        Walks along a chain of aliases, promoting the first nullable join and
     
    991999            #   - this is an annotation over a model field
    9921000            # then we need to explore the joins that are required.
    9931001
    994             field, source, opts, join_list, last, _ = self.setup_joins(
     1002            field, source, opts, join_list, last, _, allow_trim_join = self.setup_joins(
    9951003                field_list, opts, self.get_initial_alias(), False)
    9961004
    9971005            # Process the join chain to see if it can be trimmed
     
    10831091        allow_many = trim or not negate
    10841092
    10851093        try:
    1086             field, target, opts, join_list, last, extra_filters = self.setup_joins(
     1094            field, target, opts, join_list, last, extra_filters, allow_trim_join = self.setup_joins(
    10871095                    parts, opts, alias, True, allow_many, allow_explicit_fk=True,
    10881096                    can_reuse=can_reuse, negate=negate,
    10891097                    process_extras=process_extras)
     
    11031111            self.promote_alias_chain(join_list)
    11041112            join_promote = True
    11051113
     1114        # If we have a one2one or many2one field, we can trim the left outer
     1115        # join from the end of a list of joins.
     1116        # In order to do this, we convert alias join type back to INNER and
     1117        # trim_joins later will do the strip for us.
     1118        if allow_trim_join and field.rel:
     1119            self.demote_alias(join_list[-1])
     1120
    11061121        # Process the join list to see if we can remove any inner joins from
    11071122        # the far end (fewer tables in a query is better).
    11081123        nonnull_comparison = (lookup_type == 'isnull' and value is False)
     
    12591274        dupe_set = set()
    12601275        exclusions = set()
    12611276        extra_filters = []
     1277        allow_trim_join = True
    12621278        int_alias = None
    12631279        for pos, name in enumerate(names):
    12641280            if int_alias is not None:
     
    12821298                    raise FieldError("Cannot resolve keyword %r into field. "
    12831299                            "Choices are: %s" % (name, ", ".join(names)))
    12841300
     1301            # presence of indirect field in the filter requires
     1302            # left outer join for isnull
     1303            if not direct and allow_trim_join:
     1304                allow_trim_join = False
     1305
    12851306            if not allow_many and (m2m or not direct):
    12861307                for alias in joins:
    12871308                    self.unref_alias(alias)
     
    13231344                extra_filters.extend(field.extra_filters(names, pos, negate))
    13241345            if direct:
    13251346                if m2m:
     1347                    # null query on m2mfield requires outer join
     1348                    allow_trim_join = False
    13261349                    # Many-to-many field defined on the current model.
    13271350                    if cached_data:
    13281351                        (table1, from_col1, to_col1, table2, from_col2,
     
    14431466            else:
    14441467                raise FieldError("Join on field %r not permitted." % name)
    14451468
    1446         return field, target, opts, joins, last, extra_filters
     1469        return field, target, opts, joins, last, extra_filters, allow_trim_join
    14471470
    14481471    def trim_joins(self, target, join_list, last, trim, nonnull_check=False):
    14491472        """
     
    16051628
    16061629        try:
    16071630            for name in field_names:
    1608                 field, target, u2, joins, u3, u4 = self.setup_joins(
     1631                field, target, u2, joins, u3, u4, allow_trim_join = self.setup_joins(
    16091632                        name.split(LOOKUP_SEP), opts, alias, False, allow_m2m,
    16101633                        True)
    16111634                final_alias = joins[-1]
     
    18871910        """
    18881911        opts = self.model._meta
    18891912        alias = self.get_initial_alias()
    1890         field, col, opts, joins, last, extra = self.setup_joins(
     1913        field, col, opts, joins, last, extra, allow_trim_join = self.setup_joins(
    18911914                start.split(LOOKUP_SEP), opts, alias, False)
    18921915        select_col = self.alias_map[joins[1]][LHS_JOIN_COL]
    18931916        select_alias = alias
Back to Top