Skip to content

Commit abf7984

Browse files
committed
Fixed #3397: You can now order by non-DB fields in the admin by telling 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
1 parent 3d164ab commit abf7984

File tree

3 files changed

+53
-17
lines changed

3 files changed

+53
-17
lines changed

django/contrib/admin/templatetags/admin_list.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,22 +84,31 @@ def result_headers(cl):
8484
header = attr.short_description
8585
except AttributeError:
8686
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.
8995
else:
9096
if isinstance(f.rel, models.ManyToOneRel) and f.null:
9197
yield {"text": f.verbose_name}
98+
continue
9299
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()]
98107

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 '')}
103112

104113
def _boolean_icon(field_val):
105114
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}

django/contrib/admin/views/main.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,10 +655,17 @@ def get_ordering(self):
655655
order_field, order_type = ordering[0], 'asc'
656656
if params.has_key(ORDER_VAR):
657657
try:
658+
field_name = lookup_opts.admin.list_display[int(params[ORDER_VAR])]
658659
try:
659-
f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])])
660+
f = lookup_opts.get_field(field_name)
660661
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
662669
else:
663670
if not isinstance(f.rel, models.ManyToOneRel) or not f.null:
664671
order_field = f.name

docs/model-api.txt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,10 +1295,30 @@ A few special cases to note about ``list_display``:
12951295

12961296
list_display = ('__str__', 'some_other_field')
12971297

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.
13021322

13031323
``list_display_links``
13041324
----------------------

0 commit comments

Comments
 (0)