Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
  • Loading branch information...
commit 6ad9c684aa23c62710047f94b8febc681cc30282 1 parent 7355fa6
Malcolm Tredinnick authored
3  django/db/models/manager.py
@@ -104,6 +104,9 @@ def values(self, *args, **kwargs):
104 104
     def update(self, *args, **kwargs):
105 105
         return self.get_query_set().update(*args, **kwargs)
106 106
 
  107
+    def reverse(self, *args, **kwargs):
  108
+        return self.get_query_set().reverse(*args, **kwargs)
  109
+
107 110
     def _insert(self, *args, **kwargs):
108 111
         return self.get_query_set()._insert(*args, **kwargs)
109 112
 
8  django/db/models/query.py
@@ -409,6 +409,14 @@ def extra(self, select=None, where=None, params=None, tables=None,
409 409
             clone.query.extra_order_by = order_by
410 410
         return clone
411 411
 
  412
+    def reverse(self):
  413
+        """
  414
+        Reverses the ordering of the queryset.
  415
+        """
  416
+        clone = self._clone()
  417
+        clone.query.standard_ordering = not clone.query.standard_ordering
  418
+        return clone
  419
+
412 420
     ###################
413 421
     # PRIVATE METHODS #
414 422
     ###################
16  django/db/models/sql/query.py
@@ -92,6 +92,7 @@ def __init__(self, model, connection, where=WhereNode):
92 92
         self.quote_cache = {}
93 93
         self.default_cols = True
94 94
         self.default_ordering = True
  95
+        self.standard_ordering = True
95 96
 
96 97
         # SQL-related attributes
97 98
         self.select = []
@@ -160,6 +161,7 @@ def clone(self, klass=None, **kwargs):
160 161
         obj.quote_cache = {}
161 162
         obj.default_cols = self.default_cols
162 163
         obj.default_ordering = self.default_ordering
  164
+        obj.standard_ordering = self.standard_ordering
163 165
         obj.select = self.select[:]
164 166
         obj.tables = self.tables[:]
165 167
         obj.where = copy.deepcopy(self.where)
@@ -487,23 +489,27 @@ def get_ordering(self):
487 489
         distinct = self.distinct
488 490
         select_aliases = self._select_aliases
489 491
         result = []
  492
+        if self.standard_ordering:
  493
+            asc, desc = ORDER_DIR['ASC']
  494
+        else:
  495
+            asc, desc = ORDER_DIR['DESC']
490 496
         for field in ordering:
491 497
             if field == '?':
492 498
                 result.append(self.connection.ops.random_function_sql())
493 499
                 continue
494 500
             if isinstance(field, int):
495 501
                 if field < 0:
496  
-                    order = 'DESC'
  502
+                    order = desc
497 503
                     field = -field
498 504
                 else:
499  
-                    order = 'ASC'
  505
+                    order = asc
500 506
                 result.append('%s %s' % (field, order))
501 507
                 continue
502 508
             if '.' in field:
503 509
                 # This came in through an extra(ordering=...) addition. Pass it
504 510
                 # on verbatim, after mapping the table name to an alias, if
505 511
                 # necessary.
506  
-                col, order = get_order_dir(field)
  512
+                col, order = get_order_dir(field, asc)
507 513
                 table, col = col.split('.', 1)
508 514
                 elt = '%s.%s' % (qn(self.table_alias(table)[0]), col)
509 515
                 if not distinct or elt in select_aliases:
@@ -512,12 +518,12 @@ def get_ordering(self):
512 518
                 # 'col' is of the form 'field' or 'field1__field2' or
513 519
                 # '-field1__field2__field', etc.
514 520
                 for table, col, order in self.find_ordering_name(field,
515  
-                        self.model._meta):
  521
+                        self.model._meta, default_order=asc):
516 522
                     elt = '%s.%s' % (qn(table), qn(col))
517 523
                     if not distinct or elt in select_aliases:
518 524
                         result.append('%s %s' % (elt, order))
519 525
             else:
520  
-                col, order = get_order_dir(field)
  526
+                col, order = get_order_dir(field, asc)
521 527
                 elt = qn(col)
522 528
                 if not distinct or elt in select_aliases:
523 529
                     result.append('%s %s' % (elt, order))
20  docs/db-api.txt
@@ -554,6 +554,26 @@ There's no way to specify whether ordering should be case sensitive. With
554 554
 respect to case-sensitivity, Django will order results however your database
555 555
 backend normally orders them.
556 556
 
  557
+``reverse()``
  558
+~~~~~~~~~~~~~
  559
+
  560
+**New in Django development version**
  561
+
  562
+If you want to reverse the order in which a queryset's elements are returned,
  563
+you can use the ``reverse()`` method. Calling ``reverse()`` a second time
  564
+restores the ordering back to the normal direction.
  565
+
  566
+To retrieve the ''last'' five items in a queryset, you could do this::
  567
+
  568
+    my_queryset.reverse()[:5]
  569
+
  570
+Note that this is not quite the same as slicing from the end of a sequence in
  571
+Python. The above example will return the last item first, then the
  572
+penultimate item and so on. If we had a Python sequence and looked at
  573
+``seq[:-5]``, we would see the fifth-last item first. Django doesn't support
  574
+that mode of access (slicing from the end), since it is not possible to do it
  575
+efficiently in SQL.
  576
+
557 577
 ``distinct()``
558 578
 ~~~~~~~~~~~~~~
559 579
 
18  tests/modeltests/ordering/models.py
@@ -48,6 +48,13 @@ def __unicode__(self):
48 48
 >>> Article.objects.order_by('pub_date', '-headline')
49 49
 [<Article: Article 1>, <Article: Article 3>, <Article: Article 2>, <Article: Article 4>]
50 50
 
  51
+# Only the last order_by has any effect (since they each override any previous
  52
+# ordering).
  53
+>>> Article.objects.order_by('id')
  54
+[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>]
  55
+>>> Article.objects.order_by('id').order_by('-headline')
  56
+[<Article: Article 4>, <Article: Article 3>, <Article: Article 2>, <Article: Article 1>]
  57
+
51 58
 # Use the 'stop' part of slicing notation to limit the results.
52 59
 >>> Article.objects.order_by('headline')[:2]
53 60
 [<Article: Article 1>, <Article: Article 2>]
@@ -65,10 +72,9 @@ def __unicode__(self):
65 72
 >>> Article.objects.order_by('?')
66 73
 [...]
67 74
 
68  
-# order_by() overrides any previous ordering, so only the last one has any
69  
-# effect.
70  
->>> Article.objects.order_by('id')
71  
-[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>]
72  
->>> Article.objects.order_by('id').order_by('-headline')
73  
-[<Article: Article 4>, <Article: Article 3>, <Article: Article 2>, <Article: Article 1>]
  75
+# Ordering can be reversed using the reverse() method on a queryset. This
  76
+# allows you to extract things like "the last two items" (reverse and then
  77
+# take the first two).
  78
+>>> Article.objects.all().reverse()[:2]
  79
+[<Article: Article 1>, <Article: Article 3>]
74 80
 """}
2  tests/regressiontests/queries/models.py
@@ -317,6 +317,8 @@ class Meta:
317 317
 [<Item: four>]
318 318
 >>> Item.objects.exclude(tags__name='t4').order_by('name').distinct()
319 319
 [<Item: one>, <Item: three>, <Item: two>]
  320
+>>> Item.objects.exclude(tags__name='t4').order_by('name').distinct().reverse()
  321
+[<Item: two>, <Item: three>, <Item: one>]
320 322
 >>> Author.objects.exclude(item__name='one').distinct().order_by('name')
321 323
 [<Author: a2>, <Author: a3>, <Author: a4>]
322 324
 

0 notes on commit 6ad9c68

Please sign in to comment.
Something went wrong with that request. Please try again.