Repository: carltongibson/django-filter

Issue:
Ordering and descending order
If I have a FilterSet where I set the 'order_by' property to True to enable filtering on all fields, the descending order does not work, since a '-fieldname' parameter is not recognized. If I set the order_by fields manually, it works. 

For example, if I have a MyModel:

``` python
    class Meta:
        model = MyModel
        fields = ['title','category']
        order_by = True
```

then the ?o=title will work, but ?o=-title will not. If I write

``` python
    class Meta:
        model = MyModel
        fields = ['title','category']
        order_by = ['title','-title']
```

isntead then it works properly.



Discussion:
Patches welcome :)


Resolving patch:
diff --git a/django_filters/filterset.py b/django_filters/filterset.py
--- a/django_filters/filterset.py
+++ b/django_filters/filterset.py
@@ -10,6 +10,7 @@
 from django.utils import six
 from django.utils.datastructures import SortedDict
 from django.utils.text import capfirst
+from django.utils.translation import ugettext as _
 
 try:
     from django.db.models.constants import LOOKUP_SEP
@@ -324,11 +325,17 @@ def get_ordering_field(self):
                     # e.g. (('field', 'Display name'), ...)
                     choices = [(f[0], f[1]) for f in self._meta.order_by]
                 else:
-                    choices = [(f, capfirst(f)) for f in self._meta.order_by]
+                    choices = [(f, _('%s (descending)' % capfirst(f[1:])) if f[0] == '-' else capfirst(f))
+                               for f in self._meta.order_by]
             else:
+                # add asc and desc field names
                 # use the filter's label if provided
-                choices = [(fltr.name or f, fltr.label or capfirst(f))
-                           for f, fltr in self.filters.items()]
+                choices = []
+                for f, fltr in self.filters.items():
+                    choices.extend([
+                        (fltr.name or f, fltr.label or capfirst(f)),
+                        ("-%s" % (fltr.name or f), _('%s (descending)' % (fltr.label or capfirst(f))))
+                    ])
             return forms.ChoiceField(label="Ordering", required=False,
                                      choices=choices)
 


Test patch:
diff --git a/tests/test_filterset.py b/tests/test_filterset.py
--- a/tests/test_filterset.py
+++ b/tests/test_filterset.py
@@ -525,3 +525,25 @@ class Meta:
         self.assertQuerysetEqual(
             f.qs, ['carl', 'alex', 'jacob', 'aaron'], lambda o: o.username)
 
+    def test_ordering_descending_set(self):
+        class F(FilterSet):
+            class Meta:
+                model = User
+                fields = ['username', 'status']
+                order_by = ['username', '-username']
+
+        f = F({'o': '-username'}, queryset=self.qs)
+        self.assertQuerysetEqual(
+            f.qs, ['jacob', 'carl', 'alex', 'aaron'], lambda o: o.username)
+
+    def test_ordering_descending_unset(self):
+        """ Test ordering descending works when order_by=True. """
+        class F(FilterSet):
+            class Meta:
+                model = User
+                fields = ['username', 'status']
+                order_by = True
+
+        f = F({'o': '-username'}, queryset=self.qs)
+        self.assertQuerysetEqual(
+            f.qs, ['jacob', 'carl', 'alex', 'aaron'], lambda o: o.username)
diff --git a/tests/test_forms.py b/tests/test_forms.py
--- a/tests/test_forms.py
+++ b/tests/test_forms.py
@@ -173,7 +173,7 @@ class Meta:
 
         f = F().form
         self.assertEqual(f.fields['o'].choices,
-            [('username', 'Username'), ('status', 'Status')])
+            [('username', 'Username'), ('-username', 'Username (descending)'), ('status', 'Status'), ('-status', 'Status (descending)')])
 
     def test_ordering_uses_filter_label(self):
         class F(FilterSet):
@@ -186,7 +186,7 @@ class Meta:
         
         f = F().form
         self.assertEqual(f.fields['o'].choices,
-            [('username', 'Account'), ('status', 'Status')])
+            [('username', 'Account'), ('-username', 'Account (descending)'), ('status', 'Status'), ('-status', 'Status (descending)')])
 
     def test_ordering_uses_implicit_filter_name(self):
         class F(FilterSet):
@@ -199,7 +199,7 @@ class Meta:
         
         f = F().form
         self.assertEqual(f.fields['o'].choices,
-            [('username', 'Account'), ('status', 'Status')])
+            [('username', 'Account'), ('-username', 'Account (descending)'), ('status', 'Status'), ('-status', 'Status (descending)')])
 
     def test_ordering_with_overridden_field_name(self):
         """
@@ -218,6 +218,24 @@ class Meta:
         self.assertNotIn('o', f.fields)
         self.assertIn('order', f.fields)
         self.assertEqual(f.fields['order'].choices, [('status', 'Status')])
+    
+    def test_ordering_with_overridden_field_name_and_descending(self):
+        """
+        Set the `order_by_field` on the queryset and ensure that the
+        field name is respected.
+        """
+        class F(FilterSet):
+            order_by_field = 'order'
+
+            class Meta:
+                model = User
+                fields = ['username', 'status']
+                order_by = ['status', '-status']
+
+        f = F().form
+        self.assertNotIn('o', f.fields)
+        self.assertIn('order', f.fields)
+        self.assertEqual(f.fields['order'].choices, [('status', 'Status'), ('-status', 'Status (descending)')])
 
     def test_ordering_with_overridden_field_name_and_using_all_fields(self):
         class F(FilterSet):
@@ -231,7 +249,7 @@ class Meta:
         f = F().form
         self.assertIn('order', f.fields)
         self.assertEqual(f.fields['order'].choices,
-            [('username', 'Username'), ('status', 'Status')])
+            [('username', 'Username'), ('-username', 'Username (descending)'), ('status', 'Status'), ('-status', 'Status (descending)')])
 
     def test_ordering_with_custom_display_names(self):
         class F(FilterSet):