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.db import backend, connection
from django.db.models.query import QuerySet
@ -51,9 +50,6 @@ class Manager(QuerySet):
def _prepare(self):
if self.klass._meta.get_latest_by:
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):
# TODO: Use weakref because of possible memory leak / circular reference.
@ -101,29 +97,6 @@ class Manager(QuerySet):
kwargs['limit'] = 1
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):
# This class ensures managers aren't accessible via model instances.
# 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.models.fields import FieldDoesNotExist
from django.db.models.fields import DateField, FieldDoesNotExist
from django.utils.datastructures import SortedDict
import copy
@ -180,6 +180,35 @@ class QuerySet(object):
_, sql, params = del_query._get_sql_clause(False)
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 #
#############################################

View File

@ -222,11 +222,11 @@ If you pass an invalid keyword argument, the function will raise ``TypeError``.
OR lookups
----------
By default, keyword argument queries are "AND"ed together. If you have more complex query
requirements (for example, you need to include an ``OR`` statement in your query), you need
By default, keyword argument queries are "AND"ed together. If you have more complex query
requirements (for example, you need to include an ``OR`` statement in your query), you need
to use ``Q`` objects.
A ``Q`` object is an instance of ``django.core.meta.Q``, used to encapsulate a collection of
A ``Q`` object is an instance of ``django.core.meta.Q``, used to encapsulate a collection of
keyword arguments. These keyword arguments are specified in the same way as keyword arguments to
the basic lookup functions like get_object() and get_list(). For example::
@ -241,14 +241,14 @@ the basic lookup functions like get_object() and get_list(). For example::
... WHERE question LIKE 'Who%' OR question LIKE 'What%'
You can compose statements of arbitrary complexity by combining ``Q`` objects with the ``&`` and ``|`` operators. Parenthetical grouping can also be used.
You can compose statements of arbitrary complexity by combining ``Q`` objects with the ``&`` and ``|`` operators. Parenthetical grouping can also be used.
One or more ``Q`` objects can then provided as arguments to the lookup functions. If multiple
``Q`` object arguments are provided to a lookup function, they will be "AND"ed together.
One or more ``Q`` objects can then provided as arguments to the lookup functions. If multiple
``Q`` object arguments are provided to a lookup function, they will be "AND"ed together.
For example::
polls.get_object(
Q(question__startswith='Who'),
Q(question__startswith='Who'),
Q(pub_date__exact=date(2005, 5, 2)) | Q(pub_date__exact=date(2005, 5, 6))
)
@ -258,8 +258,8 @@ For example::
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
If necessary, lookup functions can mix the use of ``Q`` objects and keyword arguments. All arguments
provided to a lookup function (be they keyword argument or ``Q`` object) are "AND"ed together.
However, if a ``Q`` object is provided, it must precede the definition of any keyword arguments.
provided to a lookup function (be they keyword argument or ``Q`` object) are "AND"ed together.
However, if a ``Q`` object is provided, it must precede the definition of any keyword arguments.
For example::
polls.get_object(
@ -270,16 +270,16 @@ For example::
# INVALID QUERY
polls.get_object(
question__startswith='Who',
question__startswith='Who',
Q(pub_date__exact=date(2005, 5, 2)) | Q(pub_date__exact=date(2005, 5, 6)))
... would not be valid.
... would not be valid.
A ``Q`` objects can also be provided to the ``complex`` keyword argument. For example::
polls.get_object(
complex=Q(question__startswith='Who') &
(Q(pub_date__exact=date(2005, 5, 2)) |
complex=Q(question__startswith='Who') &
(Q(pub_date__exact=date(2005, 5, 2)) |
Q(pub_date__exact=date(2005, 5, 6))
)
)
@ -549,16 +549,16 @@ deletes the object and has no return value. Example::
>>> c.delete()
Objects can also be deleted in bulk using the same query parameters that are
used for get_object and other query methods. For example::
Objects can also be deleted in bulk using the same query parameters that are
used for get_object and other query methods. For example::
>>> Polls.objects.delete(pub_date__year=2005)
would bulk delete all Polls with a year of 2005. A bulk delete call with no
parameters would theoretically delete all data in the table. To prevent
parameters would theoretically delete all data in the table. To prevent
accidental obliteration of a database, a bulk delete query with no parameters
will throw an exception. If you actually want to delete all the data in a
table, you must add a ``DELETE_ALL=True`` argument to your query.
will throw an exception. If you actually want to delete all the data in a
table, you must add a ``DELETE_ALL=True`` argument to your query.
For example::
>>> Polls.objects.delete(DELETE_ALL=True)
@ -681,41 +681,43 @@ Extra module functions
In addition to every function described in "Basic lookup functions" above, a
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
``get_FOO_list()`` function, where ``FOO`` is the name of the field. This
returns a list of ``datetime.datetime`` objects representing all available
dates of the given scope, as defined by the ``kind`` argument. ``kind`` should
be either ``"year"``, ``"month"`` or ``"day"``. Each ``datetime.datetime``
object in the result list is "truncated" to the given ``type``.
Every manager has a ``dates()`` method, which returns a list of
``datetime.datetime`` objects representing all available dates with the given
filters (if any) and of the given scope, as defined by the ``kind`` argument.
``field`` should be the name of a ``DateField`` or ``DateTimeField`` of your
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.
* ``"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.
Additional, optional keyword arguments, in the format described in
"Field lookups" above, are also accepted.
``order``, which defaults to ``'ASC'``, should be either ``"ASC"`` or ``"DESC"``.
This specifies how to order the results.
Here's an example, using the ``Poll`` model defined above::
>>> 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))
>>> 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))
>>> p2.save()
>>> polls.get_pub_date_list('year')
>>> Poll.objects.dates('pub_date', 'year')
[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)]
>>> polls.get_pub_date_list('day')
>>> Poll.objects.dates('pub_date', 'day')
[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)]
``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)
False
>>> Article.objects.get_pub_date_list('year')
>>> Article.objects.dates('pub_date', 'year')
[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)]
>>> 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)]
>>> 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)]
>>> 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)]
# Try some bad arguments to __get_date_list
>>> Article.objects.get_pub_date_list('badarg')
# Try some bad arguments to dates().
>>> 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):
...
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):
...
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 |
>>> s1 = Article.objects.filter(id__exact=1)