Permalink
Browse files

queryset-refactor: Ported DateQuerySet and ValueQuerySet over and fix…

…ed most of

the related tests.


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6486 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent bcdedbb commit 988b3bbdcb52c4e20551fc8936369a7f176f48bc @malcolmt malcolmt committed Oct 14, 2007
Showing with 165 additions and 106 deletions.
  1. +7 −5 django/db/models/base.py
  2. +28 −80 django/db/models/query.py
  3. +23 −0 django/db/models/sql/datastructures.py
  4. +107 −21 django/db/models/sql/query.py
View
@@ -338,13 +338,15 @@ def _get_FIELD_display(self, field):
def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
qn = connection.ops.quote_name
op = is_next and '>' or '<'
- where = '(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \
+ where = ['(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \
(qn(field.column), op, qn(field.column),
- qn(self._meta.db_table), qn(self._meta.pk.column), op)
+ qn(self._meta.db_table), qn(self._meta.pk.column), op)]
param = smart_str(getattr(self, field.attname))
- 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)
- q.extra(where=where, params=[param, param,
- getattr(self, self._meta.pk.attname)])
+ order_char = not is_next and '-' or ''
+ q = self.__class__._default_manager.filter(**kwargs).order_by(
+ order_char + field.name, order_char + self._meta.pk.name)
+ q = q.extra(where=where, params=[param, param,
+ getattr(self, self._meta.pk.attname)])
try:
return q[0]
except IndexError:
View
@@ -253,7 +253,6 @@ def delete(self):
def values(self, *fields):
return self._clone(klass=ValuesQuerySet, _fields=fields)
- # FIXME: Not converted yet!
def dates(self, field_name, kind, order='ASC'):
"""
Returns a list of datetime objects representing all available dates
@@ -265,8 +264,10 @@ def dates(self, field_name, kind, order='ASC'):
"'order' must be either 'ASC' or 'DESC'."
# Let the FieldDoesNotExist exception propagate.
field = self.model._meta.get_field(field_name, many_to_many=False)
- assert isinstance(field, DateField), "%r isn't a DateField." % field_name
- return self._clone(klass=DateQuerySet, _field=field, _kind=kind, _order=order)
+ assert isinstance(field, DateField), "%r isn't a DateField." \
+ % field_name
+ return self._clone(klass=DateQuerySet, _field=field, _kind=kind,
+ _order=order)
##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
@@ -389,56 +390,39 @@ def __init__(self, *args, **kwargs):
self.query.select_related = False
def iterator(self):
- try:
- select, sql, params = self._get_sql_clause()
- except EmptyResultSet:
- raise StopIteration
-
- qn = connection.ops.quote_name
-
- # self._select is a dictionary, and dictionaries' key order is
- # undefined, so we convert it to a list of tuples.
- extra_select = self._select.items()
+ extra_select = self.query.extra_select.keys()
+ extra_select.sort()
# Construct two objects -- fields and field_names.
# fields is a list of Field objects to fetch.
# field_names is a list of field names, which will be the keys in the
# resulting dictionaries.
if self._fields:
if not extra_select:
- fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields]
+ fields = [self.model._meta.get_field(f, many_to_many=False)
+ for f in self._fields]
field_names = self._fields
else:
fields = []
field_names = []
for f in self._fields:
if f in [field.name for field in self.model._meta.fields]:
- fields.append(self.model._meta.get_field(f, many_to_many=False))
+ fields.append(self.model._meta.get_field(f,
+ many_to_many=False))
field_names.append(f)
- elif not self._select.has_key(f):
- raise FieldDoesNotExist('%s has no field named %r' % (self.model._meta.object_name, f))
+ elif not self.query.extra_select.has_key(f):
+ raise FieldDoesNotExist('%s has no field named %r'
+ % (self.model._meta.object_name, f))
else: # Default to all fields.
fields = self.model._meta.fields
field_names = [f.attname for f in fields]
- columns = [f.column for f in fields]
- select = ['%s.%s' % (qn(self.model._meta.db_table), qn(c)) for c in columns]
+ self.query.add_local_columns([f.column for f in fields])
if extra_select:
- select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select])
- field_names.extend([f[0] for f in extra_select])
-
- cursor = connection.cursor()
- cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
-
- has_resolve_columns = hasattr(self, 'resolve_columns')
- while 1:
- rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
- if not rows:
- raise StopIteration
- for row in rows:
- if has_resolve_columns:
- row = self.resolve_columns(row, fields)
- yield dict(zip(field_names, row))
+ field_names.extend([f for f in extra_select])
+
+ for row in self.query.results_iter():
+ yield dict(zip(field_names, row))
def _clone(self, klass=None, **kwargs):
c = super(ValuesQuerySet, self)._clone(klass, **kwargs)
@@ -447,60 +431,19 @@ def _clone(self, klass=None, **kwargs):
class DateQuerySet(QuerySet):
def iterator(self):
- from django.db.backends.util import typecast_timestamp
- from django.db.models.fields import DateTimeField
-
- qn = connection.ops.quote_name
- self._order_by = () # Clear this because it'll mess things up otherwise.
+ self.query = self.query.clone(klass=sql.DateQuery)
+ self.query.select = []
+ self.query.add_date_select(self._field.column, self._kind, self._order)
if self._field.null:
- self._where.append('%s.%s IS NOT NULL' % \
- (qn(self.model._meta.db_table), qn(self._field.column)))
- try:
- select, sql, params = self._get_sql_clause()
- except EmptyResultSet:
- raise StopIteration
-
- table_name = qn(self.model._meta.db_table)
- field_name = qn(self._field.column)
-
- if connection.features.allows_group_by_ordinal:
- group_by = '1'
- else:
- group_by = connection.ops.date_trunc_sql(self._kind, '%s.%s' % (table_name, field_name))
-
- sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \
- (connection.ops.date_trunc_sql(self._kind, '%s.%s' % (qn(self.model._meta.db_table),
- qn(self._field.column))), sql, group_by, self._order)
- cursor = connection.cursor()
- cursor.execute(sql, params)
-
- has_resolve_columns = hasattr(self, 'resolve_columns')
- needs_datetime_string_cast = connection.features.needs_datetime_string_cast
- dates = []
- # It would be better to use self._field here instead of DateTimeField(),
- # but in Oracle that will result in a list of datetime.date instead of
- # datetime.datetime.
- fields = [DateTimeField()]
- while 1:
- rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
- if not rows:
- return dates
- for row in rows:
- date = row[0]
- if has_resolve_columns:
- date = self.resolve_columns([date], fields)[0]
- elif needs_datetime_string_cast:
- date = typecast_timestamp(str(date))
- dates.append(date)
+ self.query.add_filter(('%s__isnull' % self._field.name, True))
+ return self.query.results_iter()
def _clone(self, klass=None, **kwargs):
c = super(DateQuerySet, self)._clone(klass, **kwargs)
c._field = self._field
c._kind = self._kind
- c._order = self._order
return c
-# XXX; Everything below here is done.
class EmptyQuerySet(QuerySet):
def __init__(self, model=None):
super(EmptyQuerySet, self).__init__(model)
@@ -517,6 +460,11 @@ def _clone(self, klass=None, **kwargs):
c._result_cache = []
return c
+ def iterator(self):
+ # This slightly odd construction is because we need an empty generator
+ # (it should raise StopIteration immediately).
+ yield iter([]).next()
+
# QOperator, QAnd and QOr are temporarily retained for backwards compatibility.
# All the old functionality is now part of the 'Q' class.
class QOperator(Q):
@@ -57,3 +57,26 @@ def as_sql(self, quote_func=None):
else:
return 'COUNT(%s)' % col
+class Date(object):
+ """
+ Add a date selection column.
+ """
+ def __init__(self, col, lookup_type, date_sql_func):
+ self.col = col
+ self.lookup_type = lookup_type
+ self.date_sql_func= date_sql_func
+
+ def relabel_aliases(self, change_map):
+ c = self.col
+ if isinstance(c, (list, tuple)):
+ self.col = (change_map.get(c[0], c[0]), c[1])
+
+ def as_sql(self, quote_func=None):
+ if not quote_func:
+ quote_func = lambda x: x
+ if isinstance(self.col, (list, tuple)):
+ col = '%s.%s' % tuple([quote_func(c) for c in self.col])
+ else:
+ col = self.col
+ return self.date_sql_func(self.lookup_type, col)
+
Oops, something went wrong.

0 comments on commit 988b3bb

Please sign in to comment.