Skip to content

Commit

Permalink
queryset-refactor: Ported DateQuerySet and ValueQuerySet over and fix…
Browse files Browse the repository at this point in the history
…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
malcolmt committed Oct 14, 2007
1 parent bcdedbb commit 988b3bb
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 106 deletions.
12 changes: 7 additions & 5 deletions django/db/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
108 changes: 28 additions & 80 deletions django/db/models/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 #
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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):
Expand Down
23 changes: 23 additions & 0 deletions django/db/models/sql/datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Loading

0 comments on commit 988b3bb

Please sign in to comment.