Django

Code

Changeset 6486

Show
Ignore:
Timestamp:
10/13/07 21:12:40 (9 months ago)
Author:
mtredinnick
Message:

queryset-refactor: Ported DateQuerySet? and ValueQuerySet? over and fixed most of
the related tests.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/queryset-refactor/django/db/models/base.py

    r6382 r6486  
    339339        qn = connection.ops.quote_name 
    340340        op = is_next and '>' or '<' 
    341         where = '(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \ 
     341        where = ['(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \ 
    342342            (qn(field.column), op, qn(field.column), 
    343             qn(self._meta.db_table), qn(self._meta.pk.column), op) 
     343            qn(self._meta.db_table), qn(self._meta.pk.column), op)] 
    344344        param = smart_str(getattr(self, field.attname)) 
    345         q = self.__class__._default_manager.filter(**kwargs).order_by((not is_next and '-' or '') + field.name, (not is_next and '-' or '') + self._meta.pk.name) 
    346         q.extra(where=where, params=[param, param, 
    347             getattr(self, self._meta.pk.attname)]) 
     345        order_char = not is_next and '-' or '' 
     346        q = self.__class__._default_manager.filter(**kwargs).order_by( 
     347                order_char + field.name, order_char + self._meta.pk.name) 
     348        q = q.extra(where=where, params=[param, param, 
     349                getattr(self, self._meta.pk.attname)]) 
    348350        try: 
    349351            return q[0] 
  • django/branches/queryset-refactor/django/db/models/query.py

    r6484 r6486  
    254254        return self._clone(klass=ValuesQuerySet, _fields=fields) 
    255255 
    256     # FIXME: Not converted yet! 
    257256    def dates(self, field_name, kind, order='ASC'): 
    258257        """ 
     
    266265        # Let the FieldDoesNotExist exception propagate. 
    267266        field = self.model._meta.get_field(field_name, many_to_many=False) 
    268         assert isinstance(field, DateField), "%r isn't a DateField." % field_name 
    269         return self._clone(klass=DateQuerySet, _field=field, _kind=kind, _order=order) 
     267        assert isinstance(field, DateField), "%r isn't a DateField." \ 
     268                % field_name 
     269        return self._clone(klass=DateQuerySet, _field=field, _kind=kind, 
     270                _order=order) 
    270271 
    271272    ################################################################## 
     
    390391 
    391392    def iterator(self): 
    392         try: 
    393             select, sql, params = self._get_sql_clause() 
    394         except EmptyResultSet: 
    395             raise StopIteration 
    396  
    397         qn = connection.ops.quote_name 
    398  
    399         # self._select is a dictionary, and dictionaries' key order is 
    400         # undefined, so we convert it to a list of tuples. 
    401         extra_select = self._select.items() 
     393        extra_select = self.query.extra_select.keys() 
     394        extra_select.sort() 
    402395 
    403396        # Construct two objects -- fields and field_names. 
     
    407400        if self._fields: 
    408401            if not extra_select: 
    409                 fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 
     402                fields = [self.model._meta.get_field(f, many_to_many=False) 
     403                        for f in self._fields] 
    410404                field_names = self._fields 
    411405            else: 
     
    414408                for f in self._fields: 
    415409                    if f in [field.name for field in self.model._meta.fields]: 
    416                         fields.append(self.model._meta.get_field(f, many_to_many=False)) 
     410                        fields.append(self.model._meta.get_field(f, 
     411                                many_to_many=False)) 
    417412                        field_names.append(f) 
    418                     elif not self._select.has_key(f): 
    419                         raise FieldDoesNotExist('%s has no field named %r' % (self.model._meta.object_name, f)) 
     413                    elif not self.query.extra_select.has_key(f): 
     414                        raise FieldDoesNotExist('%s has no field named %r' 
     415                                % (self.model._meta.object_name, f)) 
    420416        else: # Default to all fields. 
    421417            fields = self.model._meta.fields 
    422418            field_names = [f.attname for f in fields] 
    423419 
    424         columns = [f.column for f in fields] 
    425         select = ['%s.%s' % (qn(self.model._meta.db_table), qn(c)) for c in columns] 
     420        self.query.add_local_columns([f.column for f in fields]) 
    426421        if extra_select: 
    427             select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select]) 
    428             field_names.extend([f[0] for f in extra_select]) 
    429  
    430         cursor = connection.cursor() 
    431         cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
    432  
    433         has_resolve_columns = hasattr(self, 'resolve_columns') 
    434         while 1: 
    435             rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
    436             if not rows: 
    437                 raise StopIteration 
    438             for row in rows: 
    439                 if has_resolve_columns: 
    440                     row = self.resolve_columns(row, fields) 
    441                 yield dict(zip(field_names, row)) 
     422            field_names.extend([f for f in extra_select]) 
     423 
     424        for row in self.query.results_iter(): 
     425            yield dict(zip(field_names, row)) 
    442426 
    443427    def _clone(self, klass=None, **kwargs): 
     
    448432class DateQuerySet(QuerySet): 
    449433    def iterator(self): 
    450         from django.db.backends.util import typecast_timestamp 
    451         from django.db.models.fields import DateTimeField 
    452  
    453         qn = connection.ops.quote_name 
    454         self._order_by = () # Clear this because it'll mess things up otherwise. 
     434        self.query = self.query.clone(klass=sql.DateQuery) 
     435        self.query.select = [] 
     436        self.query.add_date_select(self._field.column, self._kind, self._order) 
    455437        if self._field.null: 
    456             self._where.append('%s.%s IS NOT NULL' % \ 
    457                 (qn(self.model._meta.db_table), qn(self._field.column))) 
    458         try: 
    459             select, sql, params = self._get_sql_clause() 
    460         except EmptyResultSet: 
    461             raise StopIteration 
    462  
    463         table_name = qn(self.model._meta.db_table) 
    464         field_name = qn(self._field.column) 
    465  
    466         if connection.features.allows_group_by_ordinal: 
    467             group_by = '1' 
    468         else: 
    469             group_by = connection.ops.date_trunc_sql(self._kind, '%s.%s' % (table_name, field_name)) 
    470  
    471         sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ 
    472             (connection.ops.date_trunc_sql(self._kind, '%s.%s' % (qn(self.model._meta.db_table), 
    473             qn(self._field.column))), sql, group_by, self._order) 
    474         cursor = connection.cursor() 
    475         cursor.execute(sql, params) 
    476  
    477         has_resolve_columns = hasattr(self, 'resolve_columns') 
    478         needs_datetime_string_cast = connection.features.needs_datetime_string_cast 
    479         dates = [] 
    480         # It would be better to use self._field here instead of DateTimeField(), 
    481         # but in Oracle that will result in a list of datetime.date instead of 
    482         # datetime.datetime. 
    483         fields = [DateTimeField()] 
    484         while 1: 
    485             rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
    486             if not rows: 
    487                 return dates 
    488             for row in rows: 
    489                 date = row[0] 
    490                 if has_resolve_columns: 
    491                     date = self.resolve_columns([date], fields)[0] 
    492                 elif needs_datetime_string_cast: 
    493                     date = typecast_timestamp(str(date)) 
    494                 dates.append(date) 
     438            self.query.add_filter(('%s__isnull' % self._field.name, True)) 
     439        return self.query.results_iter() 
    495440 
    496441    def _clone(self, klass=None, **kwargs): 
     
    498443        c._field = self._field 
    499444        c._kind = self._kind 
    500         c._order = self._order 
    501445        return c 
    502446 
    503 # XXX; Everything below here is done. 
    504447class EmptyQuerySet(QuerySet): 
    505448    def __init__(self, model=None): 
     
    517460        c._result_cache = [] 
    518461        return c 
     462 
     463    def iterator(self): 
     464        # This slightly odd construction is because we need an empty generator 
     465        # (it should raise StopIteration immediately). 
     466        yield iter([]).next() 
    519467 
    520468# QOperator, QAnd and QOr are temporarily retained for backwards compatibility. 
  • django/branches/queryset-refactor/django/db/models/sql/datastructures.py

    r6116 r6486  
    5858            return 'COUNT(%s)' % col 
    5959 
     60class Date(object): 
     61    """ 
     62    Add a date selection column. 
     63    """ 
     64    def __init__(self, col, lookup_type, date_sql_func): 
     65        self.col = col 
     66        self.lookup_type = lookup_type 
     67        self.date_sql_func= date_sql_func 
     68 
     69    def relabel_aliases(self, change_map): 
     70        c = self.col 
     71        if isinstance(c, (list, tuple)): 
     72            self.col = (change_map.get(c[0], c[0]), c[1]) 
     73 
     74    def as_sql(self, quote_func=None): 
     75        if not quote_func: 
     76            quote_func = lambda x: x 
     77        if isinstance(self.col, (list, tuple)): 
     78            col = '%s.%s' % tuple([quote_func(c) for c in self.col]) 
     79        else: 
     80            col = self.col 
     81        return self.date_sql_func(self.lookup_type, col) 
     82 
  • django/branches/queryset-refactor/django/db/models/sql/query.py

    r6121 r6486  
    1212from django.utils import tree 
    1313from django.db.models.sql.where import WhereNode, AND, OR 
    14 from django.db.models.sql.datastructures import Count 
    15 from django.db.models.fields import FieldDoesNotExist 
     14from django.db.models.sql.datastructures import Count, Date 
     15from django.db.models.fields import FieldDoesNotExist, Field 
    1616from django.contrib.contenttypes import generic 
    1717from datastructures import EmptyResultSet 
     
    5555NONE = None 
    5656 
     57# FIXME: Add quote_name() calls around all the tables. 
    5758class Query(object): 
    5859    """ 
     
    7879        self.tables = []    # Aliases in the order they are created. 
    7980        self.where = WhereNode(self) 
     81        self.group_by = [] 
    8082        self.having = [] 
    81         self.group_by = [] 
    8283        self.order_by = [] 
    8384        self.low_mark, self.high_mark = 0, None  # Used for offset/limit 
     
    104105        return sql % params 
    105106 
    106     def clone(self, **kwargs): 
     107    def clone(self, klass=None, **kwargs): 
    107108        """ 
    108109        Creates a copy of the current instance. The 'kwargs' parameter can be 
    109110        used by clients to update attributes after copying has taken place. 
    110111        """ 
    111         obj = self.__class__(self.model, self.connection) 
     112        if not klass: 
     113            klass = self.__class__ 
     114        obj = klass(self.model, self.connection) 
    112115        obj.table_map = self.table_map.copy() 
    113116        obj.alias_map = copy.deepcopy(self.alias_map) 
     
    199202        if where: 
    200203            result.append('WHERE %s' % where) 
    201         result.append(' AND'.join(self.extra_where)) 
     204        if self.extra_where: 
     205            if not where: 
     206                result.append('WHERE') 
     207            else: 
     208                result.append('AND') 
     209            result.append(' AND'.join(self.extra_where)) 
     210 
     211        if self.group_by: 
     212            grouping = self.get_grouping() 
     213            result.append('GROUP BY %s' % ', '.join(grouping)) 
    202214 
    203215        ordering = self.get_ordering() 
     
    313325        qn = self.connection.ops.quote_name 
    314326        result = [] 
    315         if self.select
     327        if self.select or self.extra_select
    316328            for col in self.select: 
    317329                if isinstance(col, (list, tuple)): 
    318330                    result.append('%s.%s' % (qn(col[0]), qn(col[1]))) 
    319331                else: 
    320                     result.append(col.as_sql()) 
     332                    result.append(col.as_sql(quote_func=qn)) 
    321333        else: 
    322334            table_alias = self.tables[0] 
     
    332344        return result 
    333345 
     346    def get_grouping(self): 
     347        """ 
     348        Returns a tuple representing the SQL elements in the "group by" clause. 
     349        """ 
     350        qn = self.connection.ops.quote_name 
     351        result = [] 
     352        for col in self.group_by: 
     353            if isinstance(col, (list, tuple)): 
     354                result.append('%s.%s' % (qn(col[0]), qn(col[1]))) 
     355            elif hasattr(col, 'as_sql'): 
     356                result.append(col.as_sql(qn)) 
     357            else: 
     358                result.append(str(col)) 
     359        return result 
     360 
    334361    def get_ordering(self): 
    335362        """ 
     
    340367        opts = self.model._meta 
    341368        result = [] 
    342         for field in handle_legacy_orderlist(ordering)
     369        for field in ordering
    343370            if field == '?': 
    344371                result.append(self.connection.ops.random_function_sql()) 
     372                continue 
     373            if isinstance(field, int): 
     374                if field < 0: 
     375                    order = 'DESC' 
     376                    field = -field 
     377                else: 
     378                    order = 'ASC' 
     379                result.append('%s %s' % (field, order)) 
    345380                continue 
    346381            if field[0] == '-': 
     
    684719        self.low_mark, self.high_mark = 0, None 
    685720 
     721    def can_filter(self): 
     722        """ 
     723        Returns True if adding filters to this instance is still possible. 
     724 
     725        Typically, this means no limits or offsets have been put on the results. 
     726        """ 
     727        return not (self.low_mark or self.high_mark) 
     728 
     729    def add_local_columns(self, columns): 
     730        """ 
     731        Adds the given column names to the select set, assuming they come from 
     732        the root model (the one given in self.model). 
     733        """ 
     734        table = self.model._meta.db_table 
     735        self.select.extend([(table, col) for col in columns]) 
     736 
    686737    def add_ordering(self, *ordering): 
    687738        """ 
    688739        Adds items from the 'ordering' sequence to the query's "order by" 
    689         clause. 
     740        clause. These items are either field names (not column names) -- 
     741        possibly with a direction prefix ('-' or '?') -- or ordinals, 
     742        corresponding to column positions in the 'select' list. 
    690743        """ 
    691744        self.order_by.extend(ordering) 
     
    696749        """ 
    697750        self.order_by = [] 
    698  
    699     def can_filter(self): 
    700         """ 
    701         Returns True if adding filters to this instance is still possible. 
    702  
    703         Typically, this means no limits or offsets have been put on the results. 
    704         """ 
    705         return not (self.low_mark or self.high_mark) 
    706751 
    707752    def add_count_column(self): 
     
    714759        if not self.distinct: 
    715760            select = Count() 
    716             # Distinct handling is now done in Count(), so don't do it at this 
    717             # level. 
    718             self.distinct = False 
    719761        else: 
    720762            select = Count((self.table_map[self.model._meta.db_table][0], 
    721763                    self.model._meta.pk.column), True) 
     764            # Distinct handling is done in Count(), so don't do it at this 
     765            # level. 
     766            self.distinct = False 
    722767        self.select = [select] 
    723768        self.extra_select = {} 
     
    874919            self.do_query(self.model._meta.db_table, values, where) 
    875920 
     921class DateQuery(Query): 
     922    """ 
     923    A DateQuery is a normal query, except that it specifically selects a single 
     924    date field. This requires some special handling when converting the results 
     925    back to Python objects, so we put it in a separate class. 
     926    """ 
     927    def results_iter(self): 
     928        """ 
     929        Returns an iterator over the results from executing this query. 
     930        """ 
     931        resolve_columns = hasattr(self, 'resolve_columns') 
     932        if resolve_columns: 
     933            from django.db.models.fields import DateTimeField 
     934            fields = [DateTimeField()] 
     935        else: 
     936            from django.db.backends.util import typecast_timestamp 
     937            needs_string_cast = self.connection.features.needs_datetime_string_cast 
     938 
     939        for rows in self.execute_sql(MULTI): 
     940            for row in rows: 
     941                date = row[0] 
     942                if resolve_columns: 
     943                    date = self.resolve_columns([date], fields)[0] 
     944                elif needs_string_cast: 
     945                    date = typecast_timestamp(str(date)) 
     946                yield date 
     947 
     948    def add_date_select(self, column, lookup_type, order='ASC'): 
     949        """ 
     950        Converts the query into a date extraction query. 
     951        """ 
     952        alias = self.join((None, self.model._meta.db_table, None, None)) 
     953        select = Date((alias, column), lookup_type, 
     954                self.connection.ops.date_trunc_sql) 
     955        self.select = [select] 
     956        self.order_by = order == 'ASC' and [1] or [-1] 
     957        if self.connection.features.allows_group_by_ordinal: 
     958            self.group_by = [1] 
     959        else: 
     960            self.group_by = [select] 
     961 
    876962def find_field(name, field_list, related_query): 
    877963    """