1
0
mirror of https://github.com/django/django.git synced 2024-12-22 17:16:24 +00:00

Fixed #22550 -- Prohibited QuerySet.last()/reverse() after slicing.

This commit is contained in:
Matthias Erll 2014-05-17 14:59:57 +02:00 committed by Tim Graham
parent 84fb50df67
commit eee34ef64c
5 changed files with 26 additions and 1 deletions

View File

@ -944,6 +944,8 @@ class QuerySet:
def reverse(self): def reverse(self):
"""Reverse the ordering of the QuerySet.""" """Reverse the ordering of the QuerySet."""
if not self.query.can_filter():
raise TypeError('Cannot reverse a query once a slice has been taken.')
clone = self._clone() clone = self._clone()
clone.query.standard_ordering = not clone.query.standard_ordering clone.query.standard_ordering = not clone.query.standard_ordering
return clone return clone

View File

@ -314,6 +314,18 @@ If you wish to keep this restriction in the admin when editing users, set
admin.site.unregister(User) admin.site.unregister(User)
admin.site.register(User, MyUserAdmin) admin.site.register(User, MyUserAdmin)
``QuerySet.reverse()`` and ``last()`` are prohibited after slicing
------------------------------------------------------------------
Calling ``QuerySet.reverse()`` or ``last()`` on a sliced queryset leads to
unexpected results due to the slice being applied after reordering. This is
now prohibited, e.g.::
>>> Model.objects.all()[:2].reverse()
Traceback (most recent call last):
...
TypeError: Cannot reverse a query once a slice has been taken.
Miscellaneous Miscellaneous
------------- -------------

View File

@ -361,6 +361,9 @@ every *second* object of the first 10::
>>> Entry.objects.all()[:10:2] >>> Entry.objects.all()[:10:2]
Further filtering or ordering of a sliced queryset is prohibited due to the
ambiguous nature of how that might work.
To retrieve a *single* object rather than a list To retrieve a *single* object rather than a list
(e.g. ``SELECT foo FROM bar LIMIT 1``), use a simple index instead of a (e.g. ``SELECT foo FROM bar LIMIT 1``), use a simple index instead of a
slice. For example, this returns the first ``Entry`` in the database, after slice. For example, this returns the first ``Entry`` in the database, after

View File

@ -203,6 +203,14 @@ class OrderingTests(TestCase):
attrgetter("headline") attrgetter("headline")
) )
def test_no_reordering_after_slicing(self):
msg = 'Cannot reverse a query once a slice has been taken.'
qs = Article.objects.all()[0:2]
with self.assertRaisesMessage(TypeError, msg):
qs.reverse()
with self.assertRaisesMessage(TypeError, msg):
qs.last()
def test_extra_ordering(self): def test_extra_ordering(self):
""" """
Ordering can be based on fields included from an 'extra' clause Ordering can be based on fields included from an 'extra' clause

View File

@ -731,10 +731,10 @@ class Queries1Tests(TestCase):
q.extra(select={'foo': "1"}), q.extra(select={'foo': "1"}),
[] []
) )
self.assertQuerysetEqual(q.reverse(), [])
q.query.low_mark = 1 q.query.low_mark = 1
with self.assertRaisesMessage(AssertionError, 'Cannot change a query once a slice has been taken'): with self.assertRaisesMessage(AssertionError, 'Cannot change a query once a slice has been taken'):
q.extra(select={'foo': "1"}) q.extra(select={'foo': "1"})
self.assertQuerysetEqual(q.reverse(), [])
self.assertQuerysetEqual(q.defer('meal'), []) self.assertQuerysetEqual(q.defer('meal'), [])
self.assertQuerysetEqual(q.only('meal'), []) self.assertQuerysetEqual(q.only('meal'), [])