django :: list_display can’t sort on attribute of foreign key field…

I’m not the only one surprised by the fact that you can’t use the double-underscore-foreignkey-attribute-accessor syntax in list_display:

class MyModelOptions(admin.ModelAdmin):
    list_display = ['fk_field__fk_attribute']  # ILLEGAL

There has been extensive discussions on the tracker ( and on the mailing list ( It seems unlikely that this will be possible to do before hell freezes over (although someone commented that it works in Django 1.2 here:

Luke Plant has suggested a solution using callables, which I personally find ugly, but YMMW:

def foreign_field(field_name):
    def accessor(obj):
        val = obj
        for part in field_name.split('__'):
            val = getattr(val, part)
        return val
    accessor.__name__ = field_name
    return accessor

ff = foreign_field

class MyAdmin(ModelAdmin):

    list_display = [ff('foreign_key__related_fieldname1'),

The code from @lukeplant does all the work in the ModelAdmin, which keeps the Model class free of extra methods for the admin. This is generally a good idea, however I just had a use case where adding accessors made working with the model much easier.

The model in question looks like this:

class DailyEmployeeProjectHours(models.Model):
    employee = models.ForeignKey(Employee)
    empday = models.ForeignKey(EmployeeDay)
    project = models.ForeignKey(Project)
    seconds_worked = models.IntegerField(default=0)

The code was littered with “deph.employee.user.username”, “”, and “”…

We needed something that could define an accessor/property, that would automagically be sortable in Django’s admin interface… which sounds like the job for a descriptor:

class FkeyLookup(object):
    def __init__(self, fkeydecl, short_description=None, admin_order_field=None):, fkattrs = fkeydecl.split('__', 1)
        self.fkattrs = fkattrs.split('__')

        self.short_description = short_description or self.fkattrs[-1]
        self.admin_order_field = admin_order_field or fkeydecl

    def __get__(self, obj, klass):
        if obj is None:
            return self  # hack required to make Django validate (if obj is None, then we're a class, and classes are callable <wink>)

        item = getattr(obj,
        for attr in self.fkattrs:
            item = getattr(item, attr)
        return item


class DailyEmployeeProjectHours(models.Model):
    employee = models.ForeignKey(Employee)
    username = FkeyLookup("employee__user__username")

    empday = models.ForeignKey(EmployeeDay)
    date = FkeyLookup("empday__date")

    project = models.ForeignKey(Project)
    name = FkeyLookup("project__name")

    seconds_worked = models.IntegerField(default=0)

and in

class DailyEmployeeProjectHoursOptions(admin.ModelAdmin):
    list_display = "username date name hours".split()

    def hours(self, obj):
        return '%.2f' % round(obj.seconds_worked / 3600.0, 2)
    hours.admin_order_field = 'seconds_worked'

The admin list for EmployeeProjectHours now has columns named “Username”, “Date”, “Name”, and “Hours” — and all of them will be sortable! (if you’re on an ancient version of Django, you’ll need to apply r9212 from Django trunk, only the changes in contrib/admin/views/ are needed if you’re lazy).

In addition the DailyEmployeeProjectHours class now has a number of new properties…

deph = DailyEmployeeProjectHours.objects.get(...)
assert deph.username == deph.employee.user.username
assert ==
assert ==

It’s cute and it uses descriptors… :-)

This entry was posted in django and tagged . Bookmark the permalink.

2 Responses to django :: list_display can’t sort on attribute of foreign key field…

  1. Anika Dejong Butler says:

    I think the list_display needs to be a list, not a string…

Leave a Reply

Your email address will not be published. Required fields are marked *