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
This commit is contained in:
Jacob Kaplan-Moss 2007-02-26 05:37:24 +00:00
parent 3d164ab04f
commit abf79841fe
3 changed files with 53 additions and 17 deletions

View File

@ -84,22 +84,31 @@ def result_headers(cl):
header = attr.short_description
except AttributeError:
header = field_name.replace('_', ' ')
# Non-field list_display values don't get ordering capability.
yield {"text": header}
# It is a non-field, but perhaps one that is sortable
if not getattr(getattr(cl.model, field_name), "admin_order_field", None):
yield {"text": header}
continue
# So this _is_ a sortable non-field. Go to the yield
# after the else clause.
else:
if isinstance(f.rel, models.ManyToOneRel) and f.null:
yield {"text": f.verbose_name}
continue
else:
th_classes = []
new_order_type = 'asc'
if field_name == cl.order_field:
th_classes.append('sorted %sending' % cl.order_type.lower())
new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()]
header = f.verbose_name
yield {"text": f.verbose_name,
"sortable": True,
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
th_classes = []
new_order_type = 'asc'
if field_name == cl.order_field:
th_classes.append('sorted %sending' % cl.order_type.lower())
new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()]
yield {"text": header,
"sortable": True,
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
def _boolean_icon(field_val):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}

View File

@ -655,10 +655,17 @@ class ChangeList(object):
order_field, order_type = ordering[0], 'asc'
if params.has_key(ORDER_VAR):
try:
field_name = lookup_opts.admin.list_display[int(params[ORDER_VAR])]
try:
f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])])
f = lookup_opts.get_field(field_name)
except models.FieldDoesNotExist:
pass
# see if field_name is a name of a non-field
# that allows sorting
try:
attr = getattr(lookup_opts.admin.manager.model, field_name)
order_field = attr.admin_order_field
except IndexError:
pass
else:
if not isinstance(f.rel, models.ManyToOneRel) or not f.null:
order_field = f.name

View File

@ -1295,10 +1295,30 @@ A few special cases to note about ``list_display``:
list_display = ('__str__', 'some_other_field')
* For any element of ``list_display`` that is not a field on the model, the
change list page will not allow ordering by that column. This is because
ordering is done at the database level, and Django has no way of knowing
how to order the result of a custom method at the SQL level.
* Usually, elements of ``list_display`` that aren't actual database fields
can't be used in sorting (because Django does all the sorting at the
database level).
However, if an element of ``list_display`` represents a certain database
field, you can indicate this fact by setting the ``admin_order_field``
attribute of the item.
For example::
class Person(models.Model):
first_name = models.CharField(maxlength=50)
color_code = models.CharField(maxlength=6)
class Admin:
list_display = ('first_name', 'colored_first_name')
def colored_first_name(self):
return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name)
colored_first_name.allow_tags = True
colored_first_name.admin_order_field = 'first_name'
The above will tell Django to order by the ``first_name`` field when
trying to sort by ``colored_first_name`` in the admin.
``list_display_links``
----------------------