Browse Source

Fixed #28335 -- Allowed query expressions in Meta.ordering.

Dima Kudosh 7 years ago
parent
commit
093fd479d6

+ 2 - 2
django/db/models/base.py

@@ -1553,8 +1553,8 @@ class Model(metaclass=ModelBase):
         errors = []
         fields = cls._meta.ordering
 
-        # Skip '?' fields.
-        fields = (f for f in fields if f != '?')
+        # Skip expressions and '?' fields.
+        fields = (f for f in fields if isinstance(f, str) and f != '?')
 
         # Convert "-field" to "field".
         fields = ((f[1:] if f.startswith('-') else f) for f in fields)

+ 15 - 3
docs/ref/models/options.txt

@@ -256,9 +256,10 @@ Django quotes column and table names behind the scenes.
 
         ordering = ['-order_date']
 
-    This is a tuple or list of strings. Each string is a field name with an optional
-    "-" prefix, which indicates descending order. Fields without a leading "-" will
-    be ordered ascending. Use the string "?" to order randomly.
+    This is a tuple or list of strings and/or query expressions. Each string is
+    a field name with an optional "-" prefix, which indicates descending order.
+    Fields without a leading "-" will be ordered ascending. Use the string "?"
+    to order randomly.
 
     For example, to order by a ``pub_date`` field ascending, use this::
 
@@ -272,9 +273,20 @@ Django quotes column and table names behind the scenes.
 
         ordering = ['-pub_date', 'author']
 
+    You can also use :doc:`query expressions </ref/models/expressions>`. To
+    order by ``author`` ascending and make null values sort last, use this::
+
+        from django.db.models import F
+
+        ordering = [F('author').asc(nulls_last=True)]
+
     Default ordering also affects :ref:`aggregation queries
     <aggregation-ordering-interaction>`.
 
+    .. versionchanged:: 2.0
+
+        Support for query expressions was added.
+
 .. warning::
 
     Ordering is not a free operation. Each field you add to the ordering

+ 3 - 0
docs/releases/2.0.txt

@@ -283,6 +283,9 @@ Models
   different conditionals <conditional-aggregation>` to multiple aggregations
   over the same fields or relations.
 
+* Added support for expressions in :attr:`Meta.ordering
+  <django.db.models.Options.ordering>`.
+
 Requests and Responses
 ~~~~~~~~~~~~~~~~~~~~~~
 

+ 6 - 0
tests/ordering/models.py

@@ -42,6 +42,12 @@ class OrderedByAuthorArticle(Article):
         ordering = ('author', 'second_author')
 
 
+class OrderedByFArticle(Article):
+    class Meta:
+        proxy = True
+        ordering = (models.F('author').asc(nulls_first=True), 'id')
+
+
 class Reference(models.Model):
     article = models.ForeignKey(OrderedByAuthorArticle, models.CASCADE)
 

+ 11 - 1
tests/ordering/tests.py

@@ -5,7 +5,7 @@ from django.db.models import F
 from django.db.models.functions import Upper
 from django.test import TestCase
 
-from .models import Article, Author, Reference
+from .models import Article, Author, OrderedByFArticle, Reference
 
 
 class OrderingTests(TestCase):
@@ -368,3 +368,13 @@ class OrderingTests(TestCase):
         r1 = Reference.objects.create(article_id=self.a1.pk)
         r2 = Reference.objects.create(article_id=self.a2.pk)
         self.assertSequenceEqual(Reference.objects.all(), [r2, r1])
+
+    def test_default_ordering_by_f_expression(self):
+        """F expressions can be used in Meta.ordering."""
+        articles = OrderedByFArticle.objects.all()
+        articles.filter(headline='Article 2').update(author=self.author_2)
+        articles.filter(headline='Article 3').update(author=self.author_1)
+        self.assertQuerysetEqual(
+            articles, ['Article 1', 'Article 4', 'Article 3', 'Article 2'],
+            attrgetter('headline')
+        )