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:
parent
cf276407ea
commit
97e7ff7c27
|
@ -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.
|
||||
|
|
|
@ -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 #
|
||||
#############################################
|
||||
|
|
|
@ -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"``.
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue