Django

Code

Changeset 7835

Show
Ignore:
Timestamp:
07/04/08 01:42:58 (2 months ago)
Author:
mtredinnick
Message:

Redo the changes in [7773] in a better way.

This removes some of the leaky abstraction problems (lifting WhereNode?
internals into the Query class) from that commit and makes it possible for
extensions to WhereNode? to have access to the field instances. It's also
backwards-compatible with pre-[7773] code, which is also better.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/db/models/sql/query.py

    r7812 r7835  
    11051105                self.promote_alias(table) 
    11061106 
    1107         # To save memory and copying time, convert the value from the Python 
    1108         # object to the actual value used in the SQL query. 
    1109         if field: 
    1110             params = field.get_db_prep_lookup(lookup_type, value) 
    1111         else: 
    1112             params = Field().get_db_prep_lookup(lookup_type, value) 
    1113         if isinstance(value, datetime.datetime): 
    1114             annotation = datetime.datetime 
    1115         else: 
    1116             annotation = bool(value) 
    1117  
    1118         self.where.add((alias, col, field.db_type(), lookup_type, annotation, 
    1119             params), connector) 
     1107        self.where.add((alias, col, field, lookup_type, value), connector) 
    11201108 
    11211109        if negate: 
     
    11271115                        if self.alias_map[alias][JOIN_TYPE] == self.LOUTER: 
    11281116                            j_col = self.alias_map[alias][RHS_JOIN_COL] 
    1129                             entry = Node([(alias, j_col, None, 'isnull', True, 
    1130                                     [True])]
     1117                            entry = self.where_class() 
     1118                            entry.add((alias, j_col, None, 'isnull', True), AND
    11311119                            entry.negate() 
    11321120                            self.where.add(entry, AND) 
     
    11361124                    # exclude the "foo__in=[]" case from this handling, because 
    11371125                    # it's short-circuited in the Where class. 
    1138                     entry = Node([(alias, col, None, 'isnull', True, [True])]) 
     1126                    entry = self.where_class() 
     1127                    entry.add((alias, col, None, 'isnull', True), AND) 
    11391128                    entry.negate() 
    11401129                    self.where.add(entry, AND) 
  • django/trunk/django/db/models/sql/subqueries.py

    r7773 r7835  
    5050                    where = self.where_class() 
    5151                    where.add((None, related.field.m2m_reverse_name(), 
    52                             related.field.db_type(), 'in', True
     52                            related.field, 'in'
    5353                            pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]), 
    5454                            AND) 
     
    6060                from django.contrib.contenttypes.models import ContentType 
    6161                field = f.rel.to._meta.get_field(f.content_type_field_name) 
    62                 w1.add((None, field.column, field.db_type(), 'exact', True
    63                         [ContentType.objects.get_for_model(cls).id]), AND) 
     62                w1.add((None, field.column, field, 'exact'
     63                        ContentType.objects.get_for_model(cls).id), AND) 
    6464            for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
    6565                where = self.where_class() 
    66                 where.add((None, f.m2m_column_name(), f.db_type(), 'in', True
     66                where.add((None, f.m2m_column_name(), f, 'in'
    6767                        pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), 
    6868                        AND) 
     
    8282            where = self.where_class() 
    8383            field = self.model._meta.pk 
    84             where.add((None, field.column, field.db_type(), 'in', True
     84            where.add((None, field.column, field, 'in'
    8585                    pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), AND) 
    8686            self.do_query(self.model._meta.db_table, where) 
     
    205205            self.where = self.where_class() 
    206206            f = self.model._meta.pk 
    207             self.where.add((None, f.column, f.db_type(), 'in', True
     207            self.where.add((None, f.column, f, 'in'
    208208                    pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), 
    209209                    AND) 
  • django/trunk/django/db/models/sql/where.py

    r7774 r7835  
    2828    default = AND 
    2929 
    30     def as_sql(self, node=None, qn=None): 
     30    def add(self, data, connector): 
     31        """ 
     32        Add a node to the where-tree. If the data is a list or tuple, it is 
     33        expected to be of the form (alias, col_name, field_obj, lookup_type, 
     34        value), which is then slightly munged before being stored (to avoid 
     35        storing any reference to field objects). Otherwise, the 'data' is 
     36        stored unchanged and can be anything with an 'as_sql()' method. 
     37        """ 
     38        if not isinstance(data, (list, tuple)): 
     39            super(WhereNode, self).add(data, connector) 
     40            return 
     41 
     42        alias, col, field, lookup_type, value = data 
     43        if field: 
     44            params = field.get_db_prep_lookup(lookup_type, value) 
     45            db_type = field.db_type() 
     46        else: 
     47            # This is possible when we add a comparison to NULL sometimes (we 
     48            # don't really need to waste time looking up the associated field 
     49            # object). 
     50            params = Field().get_db_prep_lookup(lookup_type, value) 
     51            db_type = None 
     52        if isinstance(value, datetime.datetime): 
     53            annotation = datetime.datetime 
     54        else: 
     55            annotation = bool(value) 
     56        super(WhereNode, self).add((alias, col, db_type, lookup_type, 
     57                annotation, params), connector) 
     58 
     59    def as_sql(self, qn=None): 
    3160        """ 
    3261        Returns the SQL version of the where clause and the value to be 
     
    3766        recursion). 
    3867        """ 
    39         if node is None: 
    40             node = self 
    4168        if not qn: 
    4269            qn = connection.ops.quote_name 
    43         if not node.children: 
     70        if not self.children: 
    4471            return None, [] 
    4572        result = [] 
    4673        result_params = [] 
    4774        empty = True 
    48         for child in node.children: 
     75        for child in self.children: 
    4976            try: 
    5077                if hasattr(child, 'as_sql'): 
    5178                    sql, params = child.as_sql(qn=qn) 
    52                     format = '(%s)' 
    53                 elif isinstance(child, tree.Node): 
    54                     sql, params = self.as_sql(child, qn) 
    55                     if child.negated: 
    56                         format = 'NOT (%s)' 
    57                     elif len(child.children) == 1: 
    58                         format = '%s' 
    59                     else: 
    60                         format = '(%s)' 
    6179                else: 
     80                    # A leaf node in the tree. 
    6281                    sql, params = self.make_atom(child, qn) 
    63                     format = '%s' 
    6482            except EmptyResultSet: 
    65                 if node.connector == AND and not node.negated: 
     83                if self.connector == AND and not self.negated: 
    6684                    # We can bail out early in this particular case (only). 
    6785                    raise 
    68                 elif node.negated: 
     86                elif self.negated: 
    6987                    empty = False 
    7088                continue 
    7189            except FullResultSet: 
    7290                if self.connector == OR: 
    73                     if node.negated: 
     91                    if self.negated: 
    7492                        empty = True 
    7593                        break 
    7694                    # We match everything. No need for any constraints. 
    7795                    return '', [] 
    78                 if node.negated: 
     96                if self.negated: 
    7997                    empty = True 
    8098                continue 
    8199            empty = False 
    82100            if sql: 
    83                 result.append(format % sql) 
     101                result.append(sql) 
    84102                result_params.extend(params) 
    85103        if empty: 
    86104            raise EmptyResultSet 
    87         conn = ' %s ' % node.connector 
    88         return conn.join(result), result_params 
     105 
     106        conn = ' %s ' % self.connector 
     107        sql_string = conn.join(result) 
     108        if sql_string: 
     109            if self.negated: 
     110                sql_string = 'NOT (%s)' % sql_string 
     111            elif len(self.children) != 1: 
     112                sql_string = '(%s)' % sql_string 
     113        return sql_string, result_params 
    89114 
    90115    def make_atom(self, child, qn): 
    91116        """ 
    92         Turn a tuple (table_alias, field_name, db_type, lookup_type, 
     117        Turn a tuple (table_alias, column_name, db_type, lookup_type, 
    93118        value_annot, params) into valid SQL. 
    94119 
  • django/trunk/django/utils/tree.py

    r7477 r7835  
    2929        self.subtree_parents = [] 
    3030        self.negated = negated 
     31 
     32    # We need this because of django.db.models.query_utils.Q. Q. __init__() is 
     33    # problematic, but it is a natural Node subclass in all other respects. 
     34    def _new_instance(cls, children=None, connector=None, negated=False): 
     35        """ 
     36        This is called to create a new instance of this class when we need new 
     37        Nodes (or subclasses) in the internal code in this class. Normally, it 
     38        just shadows __init__(). However, subclasses with an __init__ signature 
     39        that is not an extension of Node.__init__ might need to implement this 
     40        method to allow a Node to create a new instance of them (if they have 
     41        any extra setting up to do). 
     42        """ 
     43        obj = Node(children, connector, negated) 
     44        obj.__class__ = cls 
     45        return obj 
     46    _new_instance = classmethod(_new_instance) 
    3147 
    3248    def __str__(self): 
     
    8399                self.children.append(node) 
    84100        else: 
    85             obj = Node(self.children, self.connector, self.negated) 
     101            obj = self._new_instance(self.children, self.connector, 
     102                    self.negated) 
    86103            self.connector = conn_type 
    87104            self.children = [obj, node] 
     
    97114        method is useful for implementing "not" arrangements. 
    98115        """ 
    99         self.children = [Node(self.children, self.connector, not self.negated)] 
     116        self.children = [self._new_instance(self.children, self.connector, 
     117                not self.negated)] 
    100118        self.connector = self.default 
    101119 
     
    109127            self.connector = conn_type 
    110128        elif self.connector != conn_type: 
    111             self.children = [Node(self.children, self.connector, self.negated)] 
     129            self.children = [self._new_instance(self.children, self.connector, 
     130                    self.negated)] 
    112131            self.connector = conn_type 
    113132            self.negated = False 
    114133 
    115         self.subtree_parents.append(Node(self.children, self.connector
    116                 self.negated)) 
     134        self.subtree_parents.append(self.__class__(self.children
     135                self.connector, self.negated)) 
    117136        self.connector = self.default 
    118137        self.negated = False 
     
    127146        """ 
    128147        obj = self.subtree_parents.pop() 
    129         node = Node(self.children, self.connector) 
     148        node = self.__class__(self.children, self.connector) 
    130149        self.connector = obj.connector 
    131150        self.negated = obj.negated