# python – Can “list_display” in a Django ModelAdmin display attributes of ForeignKey fields?

## The Question :

328 people think this question is useful

I have a Person model that has a foreign key relationship to Book, which has a number of fields, but I’m most concerned about author (a standard CharField).

With that being said, in my PersonAdmin model, I’d like to display book.author using list_display:

class PersonAdmin(admin.ModelAdmin):
list_display = ['book.author',]



I’ve tried all of the obvious methods for doing so, but nothing seems to work.

Any suggestions?

508 people think this answer is useful

As another option, you can do look ups like:

class UserAdmin(admin.ModelAdmin):
list_display = (..., 'get_author')

def get_author(self, obj):
return obj.book.author
get_author.short_description = 'Author'



155 people think this answer is useful

Despite all the great answers above and due to me being new to Django, I was still stuck. Here’s my explanation from a very newbie perspective.

models.py

class Author(models.Model):
name = models.CharField(max_length=255)

class Book(models.Model):
author = models.ForeignKey(Author)
title = models.CharField(max_length=255)



admin.py (Incorrect Way) – you think it would work by using ‘model__field’ to reference, but it doesn’t

class BookAdmin(admin.ModelAdmin):
model = Book
list_display = ['title', 'author__name', ]



admin.py (Correct Way) – this is how you reference a foreign key name the Django way

class BookAdmin(admin.ModelAdmin):
model = Book
list_display = ['title', 'get_name', ]

def get_name(self, obj):
return obj.author.name
get_name.admin_order_field  = 'author'  #Allows column order sorting
get_name.short_description = 'Author Name'  #Renames column head

#Filtering on side - for some reason, this works
#list_filter = ['title', 'author__name']



69 people think this answer is useful

Like the rest, I went with callables too. But they have one downside: by default, you can’t order on them. Fortunately, there is a solution for that:

# Django >= 1.8

def author(self, obj):
return obj.book.author



# Django < 1.8

def author(self):
return self.book.author



52 people think this answer is useful

Please note that adding the get_author function would slow the list_display in the admin, because showing each person would make a SQL query.

To avoid this, you need to modify get_queryset method in PersonAdmin, for example:

def get_queryset(self, request):



Before: 73 queries in 36.02ms (67 duplicated queries in admin)

After: 6 queries in 10.81ms

23 people think this answer is useful

According to the documentation, you can only display the __unicode__ representation of a ForeignKey:

Seems odd that it doesn’t support the 'book__author' style format which is used everywhere else in the DB API.

Turns out there’s a ticket for this feature, which is marked as Won’t Fix.

13 people think this answer is useful

http://djangosnippets.org/snippets/2887/

So you can do:

class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]



This is basically just doing the same thing described in the other answers, but it automatically takes care of (1) setting admin_order_field (2) setting short_description and (3) modifying the queryset to avoid a database hit for each row.

10 people think this answer is useful

You can show whatever you want in list display by using a callable. It would look like this:


def book_author(object):
return object.book.author

list_display = [book_author,]

7 people think this answer is useful

This one’s already accepted, but if there are any other dummies out there (like me) that didn’t immediately get it from the presently accepted answer, here’s a bit more detail.

The model class referenced by the ForeignKey needs to have a __unicode__ method within it, like here:

class Category(models.Model):
name = models.CharField(max_length=50)

def __unicode__(self):
return self.name



That made the difference for me, and should apply to the above scenario. This works on Django 1.0.2.

7 people think this answer is useful

There is a very easy to use package available in PyPI that handles exactly that: django-related-admin. You can also see the code in GitHub.

Using this, what you want to achieve is as simple as:

class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]



Both links contain full details of installation and usage so I won’t paste them here in case they change.

Just as a side note, if you’re already using something other than model.Admin (e.g. I was using SimpleHistoryAdmin instead), you can do this: class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin).

6 people think this answer is useful

If you have a lot of relation attribute fields to use in list_display and do not want create a function (and it’s attributes) for each one, a dirt but simple solution would be override the ModelAdmin instace __getattr__ method, creating the callables on the fly:

class DynamicLookupMixin(object):
'''
a mixin to add dynamic callable attributes like 'book__author' which
return a function that return the instance.book.author value
'''

def __getattr__(self, attr):
if ('__' in attr
and not attr.startswith('_')
and not attr.endswith('_boolean')
and not attr.endswith('_short_description')):

def dyn_lookup(instance):
# traverse all __ lookups
return reduce(lambda parent, child: getattr(parent, child),
attr.split('__'),
instance)

# get admin_order_field, boolean and short_description
dyn_lookup.boolean = getattr(self, '{}_boolean'.format(attr), False)
dyn_lookup.short_description = getattr(
self, '{}_short_description'.format(attr),
attr.replace('_', ' ').capitalize())

return dyn_lookup

# not dynamic lookup, default behaviour
return self.__getattribute__(attr)

# use examples

list_display = ['book__author', 'book__publisher__name',
'book__publisher__country']

# custom short description
book__publisher__country_short_description = 'Publisher Country'

list_display = ('name', 'category__is_new')

# to show as boolean field
category__is_new_boolean = True



Callable especial attributes like boolean and short_description must be defined as ModelAdmin attributes, eg book__author_verbose_name = 'Author name' and category__is_new_boolean = True.

The callable admin_order_field attribute is defined automatically.

Don’t forget to use the list_select_related attribute in your ModelAdmin to make Django avoid aditional queries.

4 people think this answer is useful

if you try it in Inline, you wont succeed unless:

class AddInline(admin.TabularInline):
model = MyModel
fields = ('localname',)



class MyModel(models.Model):
localization = models.ForeignKey(Localizations)

def localname(self):
return self.localization.name



-1 people think this answer is useful

AlexRobbins’ answer worked for me, except that the first two lines need to be in the model (perhaps this was assumed?), and should reference self:

def book_author(self):
return self.book.author



Then the admin part works nicely.

class CoolAdmin(admin.ModelAdmin):