1
0
mirror of https://github.com/django/django.git synced 2025-06-05 03:29:12 +00:00

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

@ -222,11 +222,11 @@ If you pass an invalid keyword argument, the function will raise ``TypeError``.
OR lookups OR lookups
---------- ----------
By default, keyword argument queries are "AND"ed together. If you have more complex query 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 requirements (for example, you need to include an ``OR`` statement in your query), you need
to use ``Q`` objects. 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 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:: 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%' ... 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 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. ``Q`` object arguments are provided to a lookup function, they will be "AND"ed together.
For example:: For example::
polls.get_object( 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)) 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') 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 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. 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. However, if a ``Q`` object is provided, it must precede the definition of any keyword arguments.
For example:: For example::
polls.get_object( polls.get_object(
@ -270,16 +270,16 @@ For example::
# INVALID QUERY # INVALID QUERY
polls.get_object( 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))) 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:: A ``Q`` objects can also be provided to the ``complex`` keyword argument. For example::
polls.get_object( polls.get_object(
complex=Q(question__startswith='Who') & complex=Q(question__startswith='Who') &
(Q(pub_date__exact=date(2005, 5, 2)) | (Q(pub_date__exact=date(2005, 5, 2)) |
Q(pub_date__exact=date(2005, 5, 6)) Q(pub_date__exact=date(2005, 5, 6))
) )
) )
@ -549,16 +549,16 @@ deletes the object and has no return value. Example::
>>> c.delete() >>> c.delete()
Objects can also be deleted in bulk using the same query parameters that are Objects can also be deleted in bulk using the same query parameters that are
used for get_object and other query methods. For example:: used for get_object and other query methods. For example::
>>> Polls.objects.delete(pub_date__year=2005) >>> Polls.objects.delete(pub_date__year=2005)
would bulk delete all Polls with a year of 2005. A bulk delete call with no 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 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 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. table, you must add a ``DELETE_ALL=True`` argument to your query.
For example:: For example::
>>> Polls.objects.delete(DELETE_ALL=True) >>> 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 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)