Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #3397: You can now order by non-DB fields in the admin by telli…

…ng Django which field to actually order by. Thanks, marcink@elksoft.pl

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4596 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit abf79841fe6cce5f1f66f915815ed98ecca44e64 1 parent 3d164ab
Jacob Kaplan-Moss authored February 26, 2007
31  django/contrib/admin/templatetags/admin_list.py
@@ -84,22 +84,31 @@ def result_headers(cl):
84 84
                     header = attr.short_description
85 85
                 except AttributeError:
86 86
                     header = field_name.replace('_', ' ')
87  
-            # Non-field list_display values don't get ordering capability.
88  
-            yield {"text": header}
  87
+
  88
+            # It is a non-field, but perhaps one that is sortable
  89
+            if not getattr(getattr(cl.model, field_name), "admin_order_field", None):
  90
+                yield {"text": header}
  91
+                continue
  92
+
  93
+            # So this _is_ a sortable non-field.  Go to the yield
  94
+            # after the else clause.
89 95
         else:
90 96
             if isinstance(f.rel, models.ManyToOneRel) and f.null:
91 97
                 yield {"text": f.verbose_name}
  98
+                continue
92 99
             else:
93  
-                th_classes = []
94  
-                new_order_type = 'asc'
95  
-                if field_name == cl.order_field:
96  
-                    th_classes.append('sorted %sending' % cl.order_type.lower())
97  
-                    new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()]
  100
+                header = f.verbose_name
  101
+
  102
+        th_classes = []
  103
+        new_order_type = 'asc'
  104
+        if field_name == cl.order_field:
  105
+            th_classes.append('sorted %sending' % cl.order_type.lower())
  106
+            new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()]
98 107
 
99  
-                yield {"text": f.verbose_name,
100  
-                       "sortable": True,
101  
-                       "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
102  
-                       "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
  108
+        yield {"text": header,
  109
+               "sortable": True,
  110
+               "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
  111
+               "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
103 112
 
104 113
 def _boolean_icon(field_val):
105 114
     BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
11  django/contrib/admin/views/main.py
@@ -655,10 +655,17 @@ def get_ordering(self):
655 655
             order_field, order_type = ordering[0], 'asc'
656 656
         if params.has_key(ORDER_VAR):
657 657
             try:
  658
+                field_name = lookup_opts.admin.list_display[int(params[ORDER_VAR])]
658 659
                 try:
659  
-                    f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])])
  660
+                    f = lookup_opts.get_field(field_name)
660 661
                 except models.FieldDoesNotExist:
661  
-                    pass
  662
+                    # see if field_name is a name of a non-field
  663
+                    # that allows sorting
  664
+                    try:
  665
+                        attr = getattr(lookup_opts.admin.manager.model, field_name)
  666
+                        order_field = attr.admin_order_field
  667
+                    except IndexError:
  668
+                        pass
662 669
                 else:
663 670
                     if not isinstance(f.rel, models.ManyToOneRel) or not f.null:
664 671
                         order_field = f.name
28  docs/model-api.txt
@@ -1295,10 +1295,30 @@ A few special cases to note about ``list_display``:
1295 1295
 
1296 1296
           list_display = ('__str__', 'some_other_field')
1297 1297
 
1298  
-    * For any element of ``list_display`` that is not a field on the model, the
1299  
-      change list page will not allow ordering by that column. This is because
1300  
-      ordering is done at the database level, and Django has no way of knowing
1301  
-      how to order the result of a custom method at the SQL level.
  1298
+    * Usually, elements of ``list_display`` that aren't actual database fields
  1299
+      can't be used in sorting (because Django does all the sorting at the
  1300
+      database level).
  1301
+      
  1302
+      However, if an element of ``list_display`` represents a certain database
  1303
+      field, you can indicate this fact by setting the ``admin_order_field``
  1304
+      attribute of the item.
  1305
+      
  1306
+      For example::
  1307
+      
  1308
+        class Person(models.Model):
  1309
+            first_name = models.CharField(maxlength=50)
  1310
+            color_code = models.CharField(maxlength=6)
  1311
+
  1312
+            class Admin:
  1313
+                list_display = ('first_name', 'colored_first_name')
  1314
+
  1315
+            def colored_first_name(self):
  1316
+                return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name)
  1317
+            colored_first_name.allow_tags = True
  1318
+            colored_first_name.admin_order_field = 'first_name'
  1319
+    
  1320
+      The above will tell Django to order by the ``first_name`` field when
  1321
+      trying to sort by ``colored_first_name`` in the admin.
1302 1322
 
1303 1323
 ``list_display_links``
1304 1324
 ----------------------

0 notes on commit abf7984

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