Ticket #10790: django1.1-ticket10790v2.patch
File django1.1-ticket10790v2.patch, 9.1 KB (added by , 15 years ago) |
---|
-
django/db/models/sql/expressions.py
39 39 self.cols[node] = query.aggregate_select[node.name] 40 40 else: 41 41 try: 42 field, source, opts, join_list, last, _ = query.setup_joins(42 field, source, opts, join_list, last, _, allow_trim_join = query.setup_joins( 43 43 field_list, query.get_meta(), 44 44 query.get_initial_alias(), False) 45 45 col, _, join_list = query.trim_joins(source, join_list, last, False) -
django/db/models/sql/query.py
991 991 pieces = name.split(LOOKUP_SEP) 992 992 if not alias: 993 993 alias = self.get_initial_alias() 994 field, target, opts, joins, last, extra = self.setup_joins(pieces,994 field, target, opts, joins, last, extra, allow_trim_join = self.setup_joins(pieces, 995 995 opts, alias, False) 996 996 alias = joins[-1] 997 997 col = target.column … … 1085 1085 self.alias_map[alias] = tuple(data) 1086 1086 return True 1087 1087 return False 1088 def demote_alias(self, alias): 1089 """ 1090 Demotes the join type of an alias to an inner join 1091 """ 1092 data = list(self.alias_map[alias]) 1093 data[JOIN_TYPE] = self.INNER 1094 self.alias_map[alias] = tuple(data) 1088 1095 1089 1096 def promote_alias_chain(self, chain, must_promote=False): 1090 1097 """ … … 1474 1481 # - this is an annotation over a model field 1475 1482 # then we need to explore the joins that are required. 1476 1483 1477 field, source, opts, join_list, last, _ = self.setup_joins(1484 field, source, opts, join_list, last, _, allow_trim_join = self.setup_joins( 1478 1485 field_list, opts, self.get_initial_alias(), False) 1479 1486 1480 1487 # Process the join chain to see if it can be trimmed … … 1571 1578 allow_many = trim or not negate 1572 1579 1573 1580 try: 1574 field, target, opts, join_list, last, extra_filters = self.setup_joins(1581 field, target, opts, join_list, last, extra_filters, allow_trim_join = self.setup_joins( 1575 1582 parts, opts, alias, True, allow_many, can_reuse=can_reuse, 1576 1583 negate=negate, process_extras=process_extras) 1577 1584 except MultiJoin, e: … … 1586 1593 # needed, as it's less efficient at the database level. 1587 1594 self.promote_alias_chain(join_list) 1588 1595 1596 # If we have a one2one or many2one field, we can trim the left outer 1597 # join from the end of a list of joins. 1598 # In order to do this, we convert alias join type back to INNER and 1599 # trim_joins later will do the strip for us. 1600 if allow_trim_join and field.rel: 1601 self.demote_alias(join_list[-1]) 1602 1589 1603 # Process the join list to see if we can remove any inner joins from 1590 1604 # the far end (fewer tables in a query is better). 1591 1605 col, alias, join_list = self.trim_joins(target, join_list, last, trim) … … 1719 1733 dupe_set = set() 1720 1734 exclusions = set() 1721 1735 extra_filters = [] 1736 allow_trim_join = True 1722 1737 for pos, name in enumerate(names): 1723 1738 try: 1724 1739 exclusions.add(int_alias) … … 1743 1758 raise FieldError("Cannot resolve keyword %r into field. " 1744 1759 "Choices are: %s" % (name, ", ".join(names))) 1745 1760 1761 # presence of indirect field in the filter requires 1762 # left outer join for isnull 1763 if not direct and allow_trim_join: 1764 allow_trim_join = False 1765 1746 1766 if not allow_many and (m2m or not direct): 1747 1767 for alias in joins: 1748 1768 self.unref_alias(alias) … … 1784 1804 extra_filters.extend(field.extra_filters(names, pos, negate)) 1785 1805 if direct: 1786 1806 if m2m: 1807 # null query on m2mfield requires outer join 1808 allow_trim_join = False 1787 1809 # Many-to-many field defined on the current model. 1788 1810 if cached_data: 1789 1811 (table1, from_col1, to_col1, table2, from_col2, … … 1893 1915 else: 1894 1916 raise FieldError("Join on field %r not permitted." % name) 1895 1917 1896 return field, target, opts, joins, last, extra_filters 1918 return field, target, opts, joins, last, extra_filters, allow_trim_join 1897 1919 1898 1920 def trim_joins(self, target, join_list, last, trim): 1899 1921 """ … … 2042 2064 2043 2065 try: 2044 2066 for name in field_names: 2045 field, target, u2, joins, u3, u4 = self.setup_joins(2067 field, target, u2, joins, u3, u4, allow_trim_join = self.setup_joins( 2046 2068 name.split(LOOKUP_SEP), opts, alias, False, allow_m2m, 2047 2069 True) 2048 2070 final_alias = joins[-1] … … 2326 2348 """ 2327 2349 opts = self.model._meta 2328 2350 alias = self.get_initial_alias() 2329 field, col, opts, joins, last, extra = self.setup_joins(2351 field, col, opts, joins, last, extra, allow_trim_join = self.setup_joins( 2330 2352 start.split(LOOKUP_SEP), opts, alias, False) 2331 2353 select_col = self.alias_map[joins[1]][LHS_JOIN_COL] 2332 2354 select_alias = alias -
tests/modeltests/null_trimjoin/models.py
1 """ 2 Do not join table when querying on isnull 3 4 """ 5 6 from django.db import models 7 8 class Category(models.Model): 9 name = models.CharField(max_length=30) 10 11 class ReporterType(models.Model): 12 name = models.CharField(max_length=30) 13 14 class 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 22 class 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 32 __test__ = {'API_TESTS':""" 33 # Create a Reporter. 34 >>> r = Reporter(name='John Smith') 35 >>> r.save() 36 37 # Create an Article. 38 >>> a = Article(headline="First", reporter=r) 39 >>> a.save() 40 41 # Create an Article without reporter 42 >>> a2 = Article(headline="Second") 43 >>> a2.save() 44 45 # querying with isnull should not join Reporter table 46 >>> q = Article.objects.filter(reporter=None) 47 >>> q.query.as_sql()[0] 48 'SELECT "null_trimjoin_article"."id", "null_trimjoin_article"."headline", "null_trimjoin_article"."reporter_id" FROM "null_trimjoin_article" WHERE "null_trimjoin_article"."reporter_id" IS NULL ORDER BY "null_trimjoin_article"."headline" ASC' 49 50 # repoter table however should be in q.query.tables 51 >>> q.query.tables 52 ['null_trimjoin_article', 'null_trimjoin_reporter'] 53 54 # querying across several tables should strip only the last join, while preserving 55 # the preceeding left outer joins 56 >>> q = Article.objects.filter(reporter__type=None) 57 >>> print q 58 [<Article: First>, <Article: Second>] 59 >>> q.query.as_sql()[0] 60 'SELECT "null_trimjoin_article"."id", "null_trimjoin_article"."headline", "null_trimjoin_article"."reporter_id" FROM "null_trimjoin_article" LEFT OUTER JOIN "null_trimjoin_reporter" ON ("null_trimjoin_article"."reporter_id" = "null_trimjoin_reporter"."id") WHERE "null_trimjoin_reporter"."type_id" IS NULL ORDER BY "null_trimjoin_article"."headline" ASC' 61 62 # querying across m2m field should not strip the m2m table from join 63 >>> q = Article.objects.filter(reporter__category__isnull=True) 64 >>> q.query.as_sql()[0] 65 'SELECT "null_trimjoin_article"."id", "null_trimjoin_article"."headline", "null_trimjoin_article"."reporter_id" FROM "null_trimjoin_article" LEFT OUTER JOIN "null_trimjoin_reporter" ON ("null_trimjoin_article"."reporter_id" = "null_trimjoin_reporter"."id") LEFT OUTER JOIN "null_trimjoin_reporter_category" ON ("null_trimjoin_reporter"."id" = "null_trimjoin_reporter_category"."reporter_id") LEFT OUTER JOIN "null_trimjoin_category" ON ("null_trimjoin_reporter_category"."category_id" = "null_trimjoin_category"."id") WHERE "null_trimjoin_category"."id" IS NULL ORDER BY "null_trimjoin_article"."headline" ASC' 66 67 # reverse querying with isnull should not strip the join 68 >>> q = Reporter.objects.filter(article__isnull=True) 69 >>> q.query.as_sql()[0] 70 'SELECT "null_trimjoin_reporter"."id", "null_trimjoin_reporter"."name", "null_trimjoin_reporter"."type_id" FROM "null_trimjoin_reporter" LEFT OUTER JOIN "null_trimjoin_article" ON ("null_trimjoin_reporter"."id" = "null_trimjoin_article"."reporter_id") WHERE "null_trimjoin_article"."id" IS NULL' 71 72 """}