magic-removal: Changed get_DATEFIELD_list() manager method to dates(), which takes the name of the date field. Updated docs and unit tests.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2177 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2006-01-30 04:36:41 +00:00
parent cf276407ea
commit 97e7ff7c27
4 changed files with 91 additions and 75 deletions

View File

@ -1,4 +1,3 @@
from django.db.models.fields import DateField
from django.utils.functional import curry from django.utils.functional import curry
from django.db import backend, connection from django.db import backend, connection
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
@ -51,9 +50,6 @@ class Manager(QuerySet):
def _prepare(self): def _prepare(self):
if self.klass._meta.get_latest_by: if self.klass._meta.get_latest_by:
self.get_latest = self.__get_latest self.get_latest = self.__get_latest
for f in self.klass._meta.fields:
if isinstance(f, DateField):
setattr(self, 'get_%s_list' % f.name, curry(self.__get_date_list, f))
def contribute_to_class(self, klass, name): def contribute_to_class(self, klass, name):
# TODO: Use weakref because of possible memory leak / circular reference. # TODO: Use weakref because of possible memory leak / circular reference.
@ -101,29 +97,6 @@ class Manager(QuerySet):
kwargs['limit'] = 1 kwargs['limit'] = 1
return self.get_object(*args, **kwargs) return self.get_object(*args, **kwargs)
def __get_date_list(self, field, kind, *args, **kwargs):
from django.db.backends.util import typecast_timestamp
assert kind in ("month", "year", "day"), "'kind' must be one of 'year', 'month' or 'day'."
order = 'ASC'
if kwargs.has_key('order'):
order = kwargs['order']
del kwargs['order']
assert order in ('ASC', 'DESC'), "'order' must be either 'ASC' or 'DESC'"
kwargs['order_by'] = () # Clear this because it'll mess things up otherwise.
if field.null:
kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % \
(backend.quote_name(self.klass._meta.db_table), backend.quote_name(field.column)))
select, sql, params = self._get_sql_clause(True, *args, **kwargs)
sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \
(backend.get_date_trunc_sql(kind, '%s.%s' % (backend.quote_name(self.klass._meta.db_table),
backend.quote_name(field.column))), sql, order)
cursor = connection.cursor()
cursor.execute(sql, params)
# We have to manually run typecast_timestamp(str()) on the results, because
# MySQL doesn't automatically cast the result of date functions as datetime
# objects -- MySQL returns the values as strings, instead.
return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
class ManagerDescriptor(object): class ManagerDescriptor(object):
# This class ensures managers aren't accessible via model instances. # This class ensures managers aren't accessible via model instances.
# For example, Poll.objects works, but poll_obj.objects raises AttributeError. # For example, Poll.objects works, but poll_obj.objects raises AttributeError.

View File

@ -1,5 +1,5 @@
from django.db import backend, connection from django.db import backend, connection
from django.db.models.fields import FieldDoesNotExist from django.db.models.fields import DateField, FieldDoesNotExist
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
import copy import copy
@ -180,6 +180,35 @@ class QuerySet(object):
_, sql, params = del_query._get_sql_clause(False) _, sql, params = del_query._get_sql_clause(False)
cursor.execute("DELETE " + sql, params) cursor.execute("DELETE " + sql, params)
def dates(self, field_name, kind, order='ASC'):
"""
Returns a list of datetime objects representing all available dates
for the given field_name, scoped to 'kind'.
"""
from django.db.backends.util import typecast_timestamp
assert kind in ("month", "year", "day"), "'kind' must be one of 'year', 'month' or 'day'."
assert order in ('ASC', 'DESC'), "'order' must be either 'ASC' or 'DESC'."
# Let the FieldDoesNotExist exception propogate.
field = self.klass._meta.get_field(field_name, many_to_many=False)
assert isinstance(field, DateField), "%r isn't a DateField." % field_name
date_query = self._clone()
date_query._order_by = () # Clear this because it'll mess things up otherwise.
if field.null:
date_query._where.append('%s.%s IS NOT NULL' % \
(backend.quote_name(self.klass._meta.db_table), backend.quote_name(field.column)))
select, sql, params = date_query._get_sql_clause(True)
sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \
(backend.get_date_trunc_sql(kind, '%s.%s' % (backend.quote_name(self.klass._meta.db_table),
backend.quote_name(field.column))), sql, order)
cursor = connection.cursor()
cursor.execute(sql, params)
# We have to manually run typecast_timestamp(str()) on the results, because
# MySQL doesn't automatically cast the result of date functions as datetime
# objects -- MySQL returns the values as strings, instead.
return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
############################################# #############################################
# PUBLIC METHODS THAT RETURN A NEW QUERYSET # # PUBLIC METHODS THAT RETURN A NEW QUERYSET #
############################################# #############################################

View File

@ -681,41 +681,43 @@ Extra module functions
In addition to every function described in "Basic lookup functions" above, a In addition to every function described in "Basic lookup functions" above, a
model module might get any or all of the following methods: model module might get any or all of the following methods:
get_FOO_list(kind, \**kwargs) dates(field, kind, order='ASC')
----------------------------- -------------------------------
For every ``DateField`` and ``DateTimeField``, the model module will have a Every manager has a ``dates()`` method, which returns a list of
``get_FOO_list()`` function, where ``FOO`` is the name of the field. This ``datetime.datetime`` objects representing all available dates with the given
returns a list of ``datetime.datetime`` objects representing all available filters (if any) and of the given scope, as defined by the ``kind`` argument.
dates of the given scope, as defined by the ``kind`` argument. ``kind`` should
be either ``"year"``, ``"month"`` or ``"day"``. Each ``datetime.datetime`` ``field`` should be the name of a ``DateField`` or ``DateTimeField`` of your
object in the result list is "truncated" to the given ``type``. model.
``kind`` should be either ``"year"``, ``"month"`` or ``"day"``. Each
``datetime.datetime`` object in the result list is "truncated" to the given
``type``.
* ``"year"`` returns a list of all distinct year values for the field. * ``"year"`` returns a list of all distinct year values for the field.
* ``"month"`` returns a list of all distinct year/month values for the field. * ``"month"`` returns a list of all distinct year/month values for the field.
* ``"day"`` returns a list of all distinct year/month/day values for the field. * ``"day"`` returns a list of all distinct year/month/day values for the field.
Additional, optional keyword arguments, in the format described in ``order``, which defaults to ``'ASC'``, should be either ``"ASC"`` or ``"DESC"``.
"Field lookups" above, are also accepted. This specifies how to order the results.
Here's an example, using the ``Poll`` model defined above:: Here's an example, using the ``Poll`` model defined above::
>>> from datetime import datetime >>> from datetime import datetime
>>> p1 = polls.Poll(slug='whatsup', question="What's up?", >>> p1 = Poll(slug='whatsup', question="What's up?",
... pub_date=datetime(2005, 2, 20), expire_date=datetime(2005, 3, 20)) ... pub_date=datetime(2005, 2, 20), expire_date=datetime(2005, 3, 20))
>>> p1.save() >>> p1.save()
>>> p2 = polls.Poll(slug='name', question="What's your name?", >>> p2 = Poll(slug='name', question="What's your name?",
... pub_date=datetime(2005, 3, 20), expire_date=datetime(2005, 4, 20)) ... pub_date=datetime(2005, 3, 20), expire_date=datetime(2005, 4, 20))
>>> p2.save() >>> p2.save()
>>> polls.get_pub_date_list('year') >>> Poll.objects.dates('pub_date', 'year')
[datetime.datetime(2005, 1, 1)] [datetime.datetime(2005, 1, 1)]
>>> polls.get_pub_date_list('month') >>> Poll.objects.dates('pub_date', 'month')
[datetime.datetime(2005, 2, 1), datetime.datetime(2005, 3, 1)] [datetime.datetime(2005, 2, 1), datetime.datetime(2005, 3, 1)]
>>> polls.get_pub_date_list('day') >>> Poll.objects.dates('pub_date', 'day')
[datetime.datetime(2005, 2, 20), datetime.datetime(2005, 3, 20)] [datetime.datetime(2005, 2, 20), datetime.datetime(2005, 3, 20)]
>>> polls.get_pub_date_list('day', question__contains='name') >>> Poll.objects.dates('pub_date', 'day', order='DESC')
[datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)]
>>> Poll.objects.filter(question__contains='name').dates('pub_date', 'day')
[datetime.datetime(2005, 3, 20)] [datetime.datetime(2005, 3, 20)]
``get_FOO_list()`` also accepts an optional keyword argument ``order``, which
should be either ``"ASC"`` or ``"DESC"``. This specifies how to order the
results. Default is ``"ASC"``.

View File

@ -174,26 +174,38 @@ True
>>> Article.objects.get(id__exact=8) == Article.objects.get(id__exact=7) >>> Article.objects.get(id__exact=8) == Article.objects.get(id__exact=7)
False False
>>> Article.objects.get_pub_date_list('year') >>> Article.objects.dates('pub_date', 'year')
[datetime.datetime(2005, 1, 1, 0, 0)] [datetime.datetime(2005, 1, 1, 0, 0)]
>>> Article.objects.get_pub_date_list('month') >>> Article.objects.dates('pub_date', 'month')
[datetime.datetime(2005, 7, 1, 0, 0)] [datetime.datetime(2005, 7, 1, 0, 0)]
>>> Article.objects.get_pub_date_list('day') >>> Article.objects.dates('pub_date', 'day')
[datetime.datetime(2005, 7, 28, 0, 0), datetime.datetime(2005, 7, 29, 0, 0), datetime.datetime(2005, 7, 30, 0, 0), datetime.datetime(2005, 7, 31, 0, 0)] [datetime.datetime(2005, 7, 28, 0, 0), datetime.datetime(2005, 7, 29, 0, 0), datetime.datetime(2005, 7, 30, 0, 0), datetime.datetime(2005, 7, 31, 0, 0)]
>>> Article.objects.get_pub_date_list('day', order='ASC') >>> Article.objects.dates('pub_date', 'day', order='ASC')
[datetime.datetime(2005, 7, 28, 0, 0), datetime.datetime(2005, 7, 29, 0, 0), datetime.datetime(2005, 7, 30, 0, 0), datetime.datetime(2005, 7, 31, 0, 0)] [datetime.datetime(2005, 7, 28, 0, 0), datetime.datetime(2005, 7, 29, 0, 0), datetime.datetime(2005, 7, 30, 0, 0), datetime.datetime(2005, 7, 31, 0, 0)]
>>> Article.objects.get_pub_date_list('day', order='DESC') >>> Article.objects.dates('pub_date', 'day', order='DESC')
[datetime.datetime(2005, 7, 31, 0, 0), datetime.datetime(2005, 7, 30, 0, 0), datetime.datetime(2005, 7, 29, 0, 0), datetime.datetime(2005, 7, 28, 0, 0)] [datetime.datetime(2005, 7, 31, 0, 0), datetime.datetime(2005, 7, 30, 0, 0), datetime.datetime(2005, 7, 29, 0, 0), datetime.datetime(2005, 7, 28, 0, 0)]
# Try some bad arguments to __get_date_list # Try some bad arguments to dates().
>>> Article.objects.get_pub_date_list('badarg')
>>> Article.objects.dates()
Traceback (most recent call last):
...
TypeError: dates() takes at least 3 arguments (1 given)
>>> Article.objects.dates('invalid_field', 'year')
Traceback (most recent call last):
...
FieldDoesNotExist: name=invalid_field
>>> Article.objects.dates('pub_date', 'bad_kind')
Traceback (most recent call last): Traceback (most recent call last):
... ...
AssertionError: 'kind' must be one of 'year', 'month' or 'day'. AssertionError: 'kind' must be one of 'year', 'month' or 'day'.
>>> Article.objects.get_pub_date_list(order='ASC')
>>> Article.objects.dates('pub_date', 'year', order='bad order')
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: __get_date_list() takes at least 3 non-keyword arguments (2 given) AssertionError: 'order' must be either 'ASC' or 'DESC'.
# You can combine queries with & and | # You can combine queries with & and |
>>> s1 = Article.objects.filter(id__exact=1) >>> s1 = Article.objects.filter(id__exact=1)