summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Erll <matthias@precisdigital.com>2014-05-17 14:59:57 +0200
committerTim Graham <timograham@gmail.com>2017-05-31 19:34:56 -0400
commiteee34ef64c026e3274c6d1b4fa2baffbc956b954 (patch)
tree1ed5087f7396fafc49560e8106cf23c6d1afd99c
parent84fb50df670c8126983c9f0fd4a5c30dbe57b684 (diff)
Fixed #22550 -- Prohibited QuerySet.last()/reverse() after slicing.
-rw-r--r--django/db/models/query.py2
-rw-r--r--docs/releases/2.0.txt12
-rw-r--r--docs/topics/db/queries.txt3
-rw-r--r--tests/ordering/tests.py8
-rw-r--r--tests/queries/tests.py2
5 files changed, 26 insertions, 1 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index a20237a229..fdf720cb8a 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -944,6 +944,8 @@ class QuerySet:
def reverse(self):
"""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.query.standard_ordering = not clone.query.standard_ordering
return clone
diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt
index 39df2d891d..044123485d 100644
--- a/docs/releases/2.0.txt
+++ b/docs/releases/2.0.txt
@@ -314,6 +314,18 @@ If you wish to keep this restriction in the admin when editing users, set
admin.site.unregister(User)
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
-------------
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index b95173773b..b6263a9f42 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -361,6 +361,9 @@ every *second* object of the first 10::
>>> 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
(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
diff --git a/tests/ordering/tests.py b/tests/ordering/tests.py
index de49626cf4..ff749331b9 100644
--- a/tests/ordering/tests.py
+++ b/tests/ordering/tests.py
@@ -203,6 +203,14 @@ class OrderingTests(TestCase):
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):
"""
Ordering can be based on fields included from an 'extra' clause
diff --git a/tests/queries/tests.py b/tests/queries/tests.py
index b69ea84345..b2d5ef5e95 100644
--- a/tests/queries/tests.py
+++ b/tests/queries/tests.py
@@ -731,10 +731,10 @@ class Queries1Tests(TestCase):
q.extra(select={'foo': "1"}),
[]
)
+ self.assertQuerysetEqual(q.reverse(), [])
q.query.low_mark = 1
with self.assertRaisesMessage(AssertionError, 'Cannot change a query once a slice has been taken'):
q.extra(select={'foo': "1"})
- self.assertQuerysetEqual(q.reverse(), [])
self.assertQuerysetEqual(q.defer('meal'), [])
self.assertQuerysetEqual(q.only('meal'), [])