Fixed #724 -- Ensured get_next_by_FOO() and get_previous_by_FOO() methods don't skip or duplicate any records in the case of duplicate values. Thanks for reporting the bug, mattycakes@gmail.com
git-svn-id: http://code.djangoproject.com/svn/django/trunk@1155 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
28bce49e59
commit
e3e37ed120
|
@ -604,8 +604,8 @@ class ModelBase(type):
|
||||||
# for all DateFields and DateTimeFields that cannot be null.
|
# for all DateFields and DateTimeFields that cannot be null.
|
||||||
# EXAMPLES: Poll.get_next_by_pub_date(), Poll.get_previous_by_pub_date()
|
# EXAMPLES: Poll.get_next_by_pub_date(), Poll.get_previous_by_pub_date()
|
||||||
if not f.null:
|
if not f.null:
|
||||||
setattr(new_class, 'get_next_by_%s' % f.name, curry(method_get_next_or_previous, new_mod.get_object, f, True))
|
setattr(new_class, 'get_next_by_%s' % f.name, curry(method_get_next_or_previous, new_mod.get_object, opts, f, True))
|
||||||
setattr(new_class, 'get_previous_by_%s' % f.name, curry(method_get_next_or_previous, new_mod.get_object, f, False))
|
setattr(new_class, 'get_previous_by_%s' % f.name, curry(method_get_next_or_previous, new_mod.get_object, opts, f, False))
|
||||||
# Add "get_thingie_list" for all DateFields and DateTimeFields.
|
# Add "get_thingie_list" for all DateFields and DateTimeFields.
|
||||||
# EXAMPLE: polls.get_pub_date_list()
|
# EXAMPLE: polls.get_pub_date_list()
|
||||||
func = curry(function_get_date_list, opts, f)
|
func = curry(function_get_date_list, opts, f)
|
||||||
|
@ -990,10 +990,13 @@ def method_get_order(ordered_obj, self):
|
||||||
|
|
||||||
# DATE-RELATED METHODS #####################
|
# DATE-RELATED METHODS #####################
|
||||||
|
|
||||||
def method_get_next_or_previous(get_object_func, field, is_next, self, **kwargs):
|
def method_get_next_or_previous(get_object_func, opts, field, is_next, self, **kwargs):
|
||||||
kwargs.setdefault('where', []).append('%s %s %%s' % (field.column, (is_next and '>' or '<')))
|
op = is_next and '>' or '<'
|
||||||
kwargs.setdefault('params', []).append(str(getattr(self, field.attname)))
|
kwargs.setdefault('where', []).append('(%s %s %%s OR (%s = %%s AND %s %s %%s))' % \
|
||||||
kwargs['order_by'] = [(not is_next and '-' or '') + field.name]
|
(field.column, op, field.column, opts.pk.column, op))
|
||||||
|
param = str(getattr(self, field.attname))
|
||||||
|
kwargs.setdefault('params', []).extend([param, param, getattr(self, opts.pk.attname)])
|
||||||
|
kwargs['order_by'] = [(not is_next and '-' or '') + field.name, (not is_next and '-' or '') + opts.pk.name]
|
||||||
kwargs['limit'] = 1
|
kwargs['limit'] = 1
|
||||||
return get_object_func(**kwargs)
|
return get_object_func(**kwargs)
|
||||||
|
|
||||||
|
|
|
@ -515,6 +515,12 @@ previous object with respect to the date field, raising the appropriate
|
||||||
Both methods accept optional keyword arguments, which should be in the format
|
Both methods accept optional keyword arguments, which should be in the format
|
||||||
described in "Field lookups" above.
|
described in "Field lookups" above.
|
||||||
|
|
||||||
|
Note that in the case of identical date values, these methods will use the ID
|
||||||
|
as a fallback check. This guarantees that no records are skipped or duplicated.
|
||||||
|
For a full example, see the `lookup API sample model_`.
|
||||||
|
|
||||||
|
.. _lookup API sample model: http://www.djangoproject.com/documentation/models/lookup/
|
||||||
|
|
||||||
get_FOO_filename()
|
get_FOO_filename()
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ API_TESTS = """
|
||||||
>>> a5.save()
|
>>> a5.save()
|
||||||
>>> a6 = articles.Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0))
|
>>> a6 = articles.Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0))
|
||||||
>>> a6.save()
|
>>> a6.save()
|
||||||
|
>>> a7 = articles.Article(headline='Article 7', pub_date=datetime(2005, 7, 27))
|
||||||
|
>>> a7.save()
|
||||||
|
|
||||||
# get_iterator() is just like get_list(), but it's a generator.
|
# get_iterator() is just like get_list(), but it's a generator.
|
||||||
>>> for a in articles.get_iterator():
|
>>> for a in articles.get_iterator():
|
||||||
|
@ -39,6 +41,7 @@ Article 6
|
||||||
Article 4
|
Article 4
|
||||||
Article 2
|
Article 2
|
||||||
Article 3
|
Article 3
|
||||||
|
Article 7
|
||||||
Article 1
|
Article 1
|
||||||
|
|
||||||
# get_iterator() takes the same lookup arguments as get_list().
|
# get_iterator() takes the same lookup arguments as get_list().
|
||||||
|
@ -48,9 +51,9 @@ Article 4
|
||||||
|
|
||||||
# get_count() returns the number of objects matching search criteria.
|
# get_count() returns the number of objects matching search criteria.
|
||||||
>>> articles.get_count()
|
>>> articles.get_count()
|
||||||
6L
|
7L
|
||||||
>>> articles.get_count(pub_date__exact=datetime(2005, 7, 27))
|
>>> articles.get_count(pub_date__exact=datetime(2005, 7, 27))
|
||||||
2L
|
3L
|
||||||
>>> articles.get_count(headline__startswith='Blah blah')
|
>>> articles.get_count(headline__startswith='Blah blah')
|
||||||
0L
|
0L
|
||||||
|
|
||||||
|
@ -67,10 +70,10 @@ Article 4
|
||||||
# dictionaries instead of object instances -- and you can specify which fields
|
# dictionaries instead of object instances -- and you can specify which fields
|
||||||
# you want to retrieve.
|
# you want to retrieve.
|
||||||
>>> articles.get_values(fields=['headline'])
|
>>> articles.get_values(fields=['headline'])
|
||||||
[{'headline': 'Article 5'}, {'headline': 'Article 6'}, {'headline': 'Article 4'}, {'headline': 'Article 2'}, {'headline': 'Article 3'}, {'headline': 'Article 1'}]
|
[{'headline': 'Article 5'}, {'headline': 'Article 6'}, {'headline': 'Article 4'}, {'headline': 'Article 2'}, {'headline': 'Article 3'}, {'headline': 'Article 7'}, {'headline': 'Article 1'}]
|
||||||
>>> articles.get_values(pub_date__exact=datetime(2005, 7, 27), fields=['id'])
|
>>> articles.get_values(pub_date__exact=datetime(2005, 7, 27), fields=['id'])
|
||||||
[{'id': 2}, {'id': 3}]
|
[{'id': 2}, {'id': 3}, {'id': 7}]
|
||||||
>>> articles.get_values(fields=['id', 'headline']) == [{'id': 5, 'headline': 'Article 5'}, {'id': 6, 'headline': 'Article 6'}, {'id': 4, 'headline': 'Article 4'}, {'id': 2, 'headline': 'Article 2'}, {'id': 3, 'headline': 'Article 3'}, {'id': 1, 'headline': 'Article 1'}]
|
>>> articles.get_values(fields=['id', 'headline']) == [{'id': 5, 'headline': 'Article 5'}, {'id': 6, 'headline': 'Article 6'}, {'id': 4, 'headline': 'Article 4'}, {'id': 2, 'headline': 'Article 2'}, {'id': 3, 'headline': 'Article 3'}, {'id': 7, 'headline': 'Article 7'}, {'id': 1, 'headline': 'Article 1'}]
|
||||||
True
|
True
|
||||||
|
|
||||||
# get_values_iterator() is just like get_values(), but it's a generator.
|
# get_values_iterator() is just like get_values(), but it's a generator.
|
||||||
|
@ -83,24 +86,40 @@ True
|
||||||
[('headline', 'Article 4'), ('id', 4)]
|
[('headline', 'Article 4'), ('id', 4)]
|
||||||
[('headline', 'Article 2'), ('id', 2)]
|
[('headline', 'Article 2'), ('id', 2)]
|
||||||
[('headline', 'Article 3'), ('id', 3)]
|
[('headline', 'Article 3'), ('id', 3)]
|
||||||
|
[('headline', 'Article 7'), ('id', 7)]
|
||||||
[('headline', 'Article 1'), ('id', 1)]
|
[('headline', 'Article 1'), ('id', 1)]
|
||||||
|
|
||||||
# Every DateField and DateTimeField creates get_next_by_FOO() and
|
# Every DateField and DateTimeField creates get_next_by_FOO() and
|
||||||
# get_previous_by_FOO() methods.
|
# get_previous_by_FOO() methods.
|
||||||
|
# In the case of identical date values, these methods will use the ID as a
|
||||||
|
# fallback check. This guarantees that no records are skipped or duplicated.
|
||||||
|
>>> a1.get_next_by_pub_date()
|
||||||
|
Article 2
|
||||||
|
>>> a2.get_next_by_pub_date()
|
||||||
|
Article 3
|
||||||
>>> a3.get_next_by_pub_date()
|
>>> a3.get_next_by_pub_date()
|
||||||
Article 4
|
Article 7
|
||||||
>>> a2.get_previous_by_pub_date()
|
|
||||||
Article 1
|
|
||||||
|
|
||||||
# get_next_by_FOO() and get_previous_by_FOO() take the time into account.
|
|
||||||
>>> a4.get_next_by_pub_date()
|
>>> a4.get_next_by_pub_date()
|
||||||
Article 6
|
Article 6
|
||||||
>>> a5.get_next_by_pub_date()
|
>>> a5.get_next_by_pub_date()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ArticleDoesNotExist: Article does not exist for ...
|
ArticleDoesNotExist: Article does not exist for ...
|
||||||
|
>>> a6.get_next_by_pub_date()
|
||||||
|
Article 5
|
||||||
|
>>> a7.get_next_by_pub_date()
|
||||||
|
Article 4
|
||||||
|
|
||||||
|
>>> a7.get_previous_by_pub_date()
|
||||||
|
Article 3
|
||||||
>>> a6.get_previous_by_pub_date()
|
>>> a6.get_previous_by_pub_date()
|
||||||
Article 4
|
Article 4
|
||||||
>>> a5.get_previous_by_pub_date()
|
>>> a5.get_previous_by_pub_date()
|
||||||
Article 6
|
Article 6
|
||||||
|
>>> a4.get_previous_by_pub_date()
|
||||||
|
Article 7
|
||||||
|
>>> a3.get_previous_by_pub_date()
|
||||||
|
Article 2
|
||||||
|
>>> a2.get_previous_by_pub_date()
|
||||||
|
Article 1
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue