diff options
| author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-02-23 01:35:34 +0000 |
|---|---|---|
| committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-02-23 01:35:34 +0000 |
| commit | 6ad9c684aa23c62710047f94b8febc681cc30282 (patch) | |
| tree | 8f4642ed94ecadfcd2790d6f9183f03ab0feb966 | |
| parent | 7355fa6b728357a3e7257fbe1175d68a0d7e2ec1 (diff) | |
queryset-refactor: Implemented the reverse() method on querysets.
Refs #5012.
git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7148 bcc190cf-cafb-0310-a4f2-bffc1f526a37
| -rw-r--r-- | django/db/models/manager.py | 3 | ||||
| -rw-r--r-- | django/db/models/query.py | 8 | ||||
| -rw-r--r-- | django/db/models/sql/query.py | 16 | ||||
| -rw-r--r-- | docs/db-api.txt | 20 | ||||
| -rw-r--r-- | tests/modeltests/ordering/models.py | 18 | ||||
| -rw-r--r-- | tests/regressiontests/queries/models.py | 2 |
6 files changed, 56 insertions, 11 deletions
diff --git a/django/db/models/manager.py b/django/db/models/manager.py index 8080480ae8..5569913c6f 100644 --- a/django/db/models/manager.py +++ b/django/db/models/manager.py @@ -104,6 +104,9 @@ class Manager(object): def update(self, *args, **kwargs): return self.get_query_set().update(*args, **kwargs) + def reverse(self, *args, **kwargs): + return self.get_query_set().reverse(*args, **kwargs) + def _insert(self, *args, **kwargs): return self.get_query_set()._insert(*args, **kwargs) diff --git a/django/db/models/query.py b/django/db/models/query.py index 1a3a0b55ec..7fa49db910 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -409,6 +409,14 @@ class _QuerySet(object): clone.query.extra_order_by = order_by return clone + def reverse(self): + """ + Reverses the ordering of the queryset. + """ + clone = self._clone() + clone.query.standard_ordering = not clone.query.standard_ordering + return clone + ################### # PRIVATE METHODS # ################### diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index f251730c48..3fa4550299 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -92,6 +92,7 @@ class Query(object): self.quote_cache = {} self.default_cols = True self.default_ordering = True + self.standard_ordering = True # SQL-related attributes self.select = [] @@ -160,6 +161,7 @@ class Query(object): obj.quote_cache = {} obj.default_cols = self.default_cols obj.default_ordering = self.default_ordering + obj.standard_ordering = self.standard_ordering obj.select = self.select[:] obj.tables = self.tables[:] obj.where = copy.deepcopy(self.where) @@ -487,23 +489,27 @@ class Query(object): distinct = self.distinct select_aliases = self._select_aliases result = [] + if self.standard_ordering: + asc, desc = ORDER_DIR['ASC'] + else: + asc, desc = ORDER_DIR['DESC'] for field in ordering: if field == '?': result.append(self.connection.ops.random_function_sql()) continue if isinstance(field, int): if field < 0: - order = 'DESC' + order = desc field = -field else: - order = 'ASC' + order = asc result.append('%s %s' % (field, order)) continue if '.' in field: # This came in through an extra(ordering=...) addition. Pass it # on verbatim, after mapping the table name to an alias, if # necessary. - col, order = get_order_dir(field) + col, order = get_order_dir(field, asc) table, col = col.split('.', 1) elt = '%s.%s' % (qn(self.table_alias(table)[0]), col) if not distinct or elt in select_aliases: @@ -512,12 +518,12 @@ class Query(object): # 'col' is of the form 'field' or 'field1__field2' or # '-field1__field2__field', etc. for table, col, order in self.find_ordering_name(field, - self.model._meta): + self.model._meta, default_order=asc): elt = '%s.%s' % (qn(table), qn(col)) if not distinct or elt in select_aliases: result.append('%s %s' % (elt, order)) else: - col, order = get_order_dir(field) + col, order = get_order_dir(field, asc) elt = qn(col) if not distinct or elt in select_aliases: result.append('%s %s' % (elt, order)) diff --git a/docs/db-api.txt b/docs/db-api.txt index d96541d58b..61bfd31882 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -554,6 +554,26 @@ There's no way to specify whether ordering should be case sensitive. With respect to case-sensitivity, Django will order results however your database backend normally orders them. +``reverse()`` +~~~~~~~~~~~~~ + +**New in Django development version** + +If you want to reverse the order in which a queryset's elements are returned, +you can use the ``reverse()`` method. Calling ``reverse()`` a second time +restores the ordering back to the normal direction. + +To retrieve the ''last'' five items in a queryset, you could do this:: + + my_queryset.reverse()[:5] + +Note that this is not quite the same as slicing from the end of a sequence in +Python. The above example will return the last item first, then the +penultimate item and so on. If we had a Python sequence and looked at +``seq[:-5]``, we would see the fifth-last item first. Django doesn't support +that mode of access (slicing from the end), since it is not possible to do it +efficiently in SQL. + ``distinct()`` ~~~~~~~~~~~~~~ diff --git a/tests/modeltests/ordering/models.py b/tests/modeltests/ordering/models.py index 5f1c3c3b81..d7435f6f74 100644 --- a/tests/modeltests/ordering/models.py +++ b/tests/modeltests/ordering/models.py @@ -48,6 +48,13 @@ __test__ = {'API_TESTS':""" >>> Article.objects.order_by('pub_date', '-headline') [<Article: Article 1>, <Article: Article 3>, <Article: Article 2>, <Article: Article 4>] +# Only the last order_by has any effect (since they each override any previous +# ordering). +>>> Article.objects.order_by('id') +[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>] +>>> Article.objects.order_by('id').order_by('-headline') +[<Article: Article 4>, <Article: Article 3>, <Article: Article 2>, <Article: Article 1>] + # Use the 'stop' part of slicing notation to limit the results. >>> Article.objects.order_by('headline')[:2] [<Article: Article 1>, <Article: Article 2>] @@ -65,10 +72,9 @@ __test__ = {'API_TESTS':""" >>> Article.objects.order_by('?') [...] -# order_by() overrides any previous ordering, so only the last one has any -# effect. ->>> Article.objects.order_by('id') -[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>] ->>> Article.objects.order_by('id').order_by('-headline') -[<Article: Article 4>, <Article: Article 3>, <Article: Article 2>, <Article: Article 1>] +# Ordering can be reversed using the reverse() method on a queryset. This +# allows you to extract things like "the last two items" (reverse and then +# take the first two). +>>> Article.objects.all().reverse()[:2] +[<Article: Article 1>, <Article: Article 3>] """} diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 22fa4f7803..72f543dab8 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -317,6 +317,8 @@ Bug #5324 [<Item: four>] >>> Item.objects.exclude(tags__name='t4').order_by('name').distinct() [<Item: one>, <Item: three>, <Item: two>] +>>> Item.objects.exclude(tags__name='t4').order_by('name').distinct().reverse() +[<Item: two>, <Item: three>, <Item: one>] >>> Author.objects.exclude(item__name='one').distinct().order_by('name') [<Author: a2>, <Author: a3>, <Author: a4>] |
