Changeset 7217
- Timestamp:
- 03/10/08 20:15:15 (9 months ago)
- Files:
-
- django/branches/queryset-refactor/django/db/models/sql/query.py (modified) (14 diffs)
- django/branches/queryset-refactor/django/db/models/sql/where.py (modified) (2 diffs)
- django/branches/queryset-refactor/django/utils/tree.py (modified) (3 diffs)
- django/branches/queryset-refactor/tests/regressiontests/queries/models.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/queryset-refactor/django/db/models/sql/query.py
r7179 r7217 20 20 from datastructures import EmptyResultSet, Empty 21 21 from constants import * 22 23 try: 24 set 25 except NameError: 26 from sets import Set as set # Python 2.3 fallback 22 27 23 28 __all__ = ['Query'] … … 574 579 Promotes the join type of an alias to an outer join if it's possible 575 580 for the join to contain NULL values on the left. 576 577 Returns True if the aliased join was promoted.578 581 """ 579 582 if self.alias_map[alias][ALIAS_NULLABLE]: 580 583 self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 581 return True582 return False583 584 584 585 def change_alias(self, old_alias, new_alias): … … 754 755 used, next, restricted) 755 756 756 def add_filter(self, filter_expr, connector=AND, negate=False, trim=False): 757 def add_filter(self, filter_expr, connector=AND, negate=False, trim=False, 758 merge_negated=False): 757 759 """ 758 760 Add a single filter to the query. The 'filter_expr' is a pair: … … 762 764 automatically trim the final join group (used internally when 763 765 constructing nested queries). 766 767 If 'merge_negated' is True, this negated filter will be merged with the 768 existing negated where node (if it exists). This is used when 769 constructing an exclude filter from combined subfilters. 764 770 """ 765 771 arg, value = filter_expr … … 814 820 alias = join[LHS_ALIAS] 815 821 col = join[LHS_JOIN_COL] 816 817 if lookup_type == 'isnull' and value is True and (len(join_list) > 1 or 818 len(join_list[0]) > 1): 822 if len(join_list[-1]) == 1: 823 join_list = join_list[:-1] 824 else: 825 join_list[-1] = join_list[-1][:-1] 826 827 if (lookup_type == 'isnull' and value is True and not negate and 828 (len(join_list) > 1 or len(join_list[0]) > 1)): 819 829 # If the comparison is against NULL, we need to use a left outer 820 830 # join when connecting to the previous model. We make that … … 847 857 self.promote_alias(table) 848 858 849 self.where.add([alias, col, field, lookup_type, value], connector) 859 entry = [alias, col, field, lookup_type, value] 860 if merge_negated: 861 # This case is when we're doing the Q2 filter in exclude(Q1, Q2). 862 # It's different from exclude(Q1).exclude(Q2). 863 for node in self.where.children: 864 if getattr(node, 'negated', False): 865 node.add(entry, connector) 866 merged = True 867 break 868 else: 869 self.where.add(entry, connector) 870 merged = False 850 871 851 872 if negate: 852 flag = False 853 for seq in join_list: 854 for join in seq: 855 if self.promote_alias(join): 856 flag = True 857 self.where.negate() 858 if flag: 859 # XXX: Change this to the field we joined against to allow 860 # for node sharing and where-tree optimisation? 861 self.where.add([alias, col, field, 'isnull', True], OR) 873 count = 0 874 for join in join_list: 875 count += len(join) 876 for alias in join: 877 self.promote_alias(alias) 878 if not merged: 879 self.where.negate() 880 if count > 1 and lookup_type != 'isnull': 881 j_col = self.alias_map[alias][ALIAS_JOIN][RHS_JOIN_COL] 882 entry = Node([[alias, j_col, None, 'isnull', True]]) 883 entry.negate() 884 self.where.add(entry, AND) 862 885 863 886 def add_q(self, q_object): … … 878 901 subtree = False 879 902 connector = AND 903 merge = False 880 904 for child in q_object.children: 881 905 if isinstance(child, Node): … … 884 908 self.where.end_subtree() 885 909 else: 886 self.add_filter(child, connector, q_object.negated) 910 self.add_filter(child, connector, q_object.negated, 911 merge_negated=merge) 912 merge = q_object.negated 887 913 connector = q_object.connector 888 914 if subtree: … … 903 929 """ 904 930 joins = [[alias]] 931 used = set() 905 932 for pos, name in enumerate(names): 933 used.update(joins[-1]) 906 934 if name == 'pk': 907 935 name = opts.pk.name … … 925 953 opts = int_model._meta 926 954 alias = self.join((alias, opts.db_table, lhs_col, 927 opts.pk.column) )955 opts.pk.column), exclusions=used) 928 956 alias_list.append(alias) 929 957 joins.append(alias_list) … … 951 979 952 980 int_alias = self.join((alias, table1, from_col1, to_col1), 953 dupe_multis, nullable=True)981 dupe_multis, used, nullable=True) 954 982 alias = self.join((int_alias, table2, from_col2, to_col2), 955 dupe_multis, nullable=True)983 dupe_multis, used, nullable=True) 956 984 joins.append([int_alias, alias]) 957 985 elif field.rel: … … 969 997 970 998 alias = self.join((alias, table, from_col, to_col), 971 nullable=field.null)999 exclusions=used, nullable=field.null) 972 1000 joins.append([alias]) 973 1001 else: … … 997 1025 998 1026 int_alias = self.join((alias, table1, from_col1, to_col1), 999 dupe_multis, nullable=True)1027 dupe_multis, used, nullable=True) 1000 1028 alias = self.join((int_alias, table2, from_col2, to_col2), 1001 dupe_multis, nullable=True)1029 dupe_multis, used, nullable=True) 1002 1030 joins.append([int_alias, alias]) 1003 1031 else: … … 1017 1045 1018 1046 alias = self.join((alias, table, from_col, to_col), 1019 dupe_multis, nullable=True)1047 dupe_multis, used, nullable=True) 1020 1048 joins.append([alias]) 1021 1049 django/branches/queryset-refactor/django/db/models/sql/where.py
r7170 r7217 6 6 from django.utils import tree 7 7 from django.db import connection 8 from django.db.models.fields import Field 8 9 from datastructures import EmptyResultSet, FullResultSet 9 10 … … 106 107 cast_sql = '%s' 107 108 108 params = field.get_db_prep_lookup(lookup_type, value) 109 if field: 110 params = field.get_db_prep_lookup(lookup_type, value) 111 else: 112 params = Field().get_db_prep_lookup(lookup_type, value) 109 113 if isinstance(params, tuple): 110 114 extra, params = params django/branches/queryset-refactor/django/utils/tree.py
r7169 r7217 17 17 18 18 def __init__(self, children=None, connector=None, negated=False): 19 """ 20 Constructs a new Node. If no connector is given, the default will be 21 used. 22 23 Warning: You probably don't want to pass in the 'negated' parameter. It 24 is NOT the same as constructing a node and calling negate() on the 25 result. 26 """ 19 27 self.children = children and children[:] or [] 20 28 self.connector = connector or self.default … … 64 72 connector is created, connecting the existing tree and the new node. 65 73 """ 74 if node in self.children: 75 return 66 76 if len(self.children) < 2: 67 77 self.connector = conn_type … … 79 89 def negate(self): 80 90 """ 81 Negate the sense of the root connector. 91 Negate the sense of the root connector. This reorganises the children 92 so that the current node has a single child: a negated node containing 93 all the previous children. This slightly odd construction makes adding 94 new children behave more intuitively. 82 95 83 96 Interpreting the meaning of this negate is up to client code. This django/branches/queryset-refactor/tests/regressiontests/queries/models.py
r7174 r7217 313 313 [<Author: a1>] 314 314 315 Bug #5324 315 Bug #5324, #6704 316 316 >>> Item.objects.filter(tags__name='t4') 317 317 [<Item: four>] … … 340 340 >>> len([x[2][2] for x in qs.query.alias_map.values() if x[2][2] == query.LOUTER]) 341 341 1 342 343 The previous changes shouldn't affect nullable foreign key joins. 344 >>> Tag.objects.filter(parent__isnull=True).order_by('name') 345 [<Tag: t1>] 346 >>> Tag.objects.exclude(parent__isnull=True).order_by('name') 347 [<Tag: t2>, <Tag: t3>, <Tag: t4>, <Tag: t5>] 348 >>> Tag.objects.exclude(Q(parent__name='t1') | Q(parent__isnull=True)).order_by('name') 349 [<Tag: t4>, <Tag: t5>] 350 >>> Tag.objects.exclude(Q(parent__isnull=True) | Q(parent__name='t1')).order_by('name') 351 [<Tag: t4>, <Tag: t5>] 352 >>> Tag.objects.exclude(Q(parent__parent__isnull=True)).order_by('name') 353 [<Tag: t4>, <Tag: t5>] 354 >>> Tag.objects.filter(~Q(parent__parent__isnull=True)).order_by('name') 355 [<Tag: t4>, <Tag: t5>] 342 356 343 357 Bug #2091
