Can "list_display" in a Django ModelAdmin display attributes of ForeignKey fields?
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?
Asked by: Lana241 | Posted: 28-01-2022
Answer 1
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'
get_author.admin_order_field = 'book__author'
Since Django 3.2 you can use display()
decorator:
class UserAdmin(admin.ModelAdmin):
list_display = (..., 'get_author')
@display(ordering='book__author', description='Author')
def get_author(self, obj):
return obj.book.author
Answered by: Kelsey352 | Posted: 01-03-2022
Answer 2
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.site.register(Book, BookAdmin)
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']
admin.site.register(Book, BookAdmin)
For additional reference, see the Django model link here
Answered by: Connie365 | Posted: 01-03-2022Answer 3
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
author.admin_order_field = 'book__author'
Django < 1.8
def author(self):
return self.book.author
author.admin_order_field = 'book__author'
Answered by: Nicole993 | Posted: 01-03-2022
Answer 4
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):
return super(PersonAdmin,self).get_queryset(request).select_related('book')
Answered by: Elian263 | Posted: 01-03-2022Before: 73 queries in 36.02ms (67 duplicated queries in admin)
After: 6 queries in 10.81ms
Answer 5
For Django >= 3.2
The proper way to do it with Django 3.2 or higher is by using the display decorator
class BookAdmin(admin.ModelAdmin):
model = Book
list_display = ['title', 'get_author_name']
@admin.display(description='Author Name', ordering='author__name')
def get_author_name(self, obj):
return obj.author.name
Answered by: Luke928 | Posted: 01-03-2022
Answer 6
According to the documentation, you can only display the __unicode__
representation of a ForeignKey:
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#list-display
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.
Answered by: Connie872 | Posted: 01-03-2022Answer 7
I just posted a snippet that makes admin.ModelAdmin support '__' syntax:
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.
Answered by: Adrian341 | Posted: 01-03-2022Answer 8
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 class PersonAdmin(admin.ModelAdmin): list_display = [book_author,]Answered by: Catherine446 | Posted: 01-03-2022
Answer 9
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)
.
Answer 10
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.
Answered by: Madaline168 | Posted: 01-03-2022Answer 11
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.admin_order_field = attr
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
@admin.register(models.Person)
class PersonAdmin(admin.ModelAdmin, DynamicLookupMixin):
list_display = ['book__author', 'book__publisher__name',
'book__publisher__country']
# custom short description
book__publisher__country_short_description = 'Publisher Country'
@admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin, DynamicLookupMixin):
list_display = ('name', 'category__is_new')
# to show as boolean field
category__is_new_boolean = True
As gist here
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.
Answer 12
if you try it in Inline, you wont succeed unless:
in your inline:
class AddInline(admin.TabularInline):
readonly_fields = ['localname',]
model = MyModel
fields = ('localname',)
in your model (MyModel):
class MyModel(models.Model):
localization = models.ForeignKey(Localizations)
def localname(self):
return self.localization.name
Answered by: Rubie613 | Posted: 01-03-2022
Answer 13
I may be late, but this is another way to do it. You can simply define a method in your model and access it via the list_display
as below:
models.py
class Person(models.Model):
book = models.ForeignKey(Book, on_delete=models.CASCADE)
def get_book_author(self):
return self.book.author
admin.py
class PersonAdmin(admin.ModelAdmin):
list_display = ('get_book_author',)
But this and the other approaches mentioned above add two extra queries per row in your listview page. To optimize this, we can override the get_queryset
to annotate the required field, then use the annotated field in our ModelAdmin method
admin.py
from django.db.models.expressions import F
@admin.register(models.Person)
class PersonAdmin(admin.ModelAdmin):
list_display = ('get_author',)
def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset = queryset.annotate(
_author = F('book__author')
)
return queryset
@admin.display(ordering='_author', description='Author')
def get_author(self, obj):
return obj._author
Answered by: Carlos864 | Posted: 01-03-2022
Answer 14
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.
Answered by: John440 | Posted: 01-03-2022Answer 15
I prefer this:
class CoolAdmin(admin.ModelAdmin):
list_display = ('pk', 'submodel__field')
@staticmethod
def submodel__field(obj):
return obj.submodel.field
Answered by: Tara358 | Posted: 01-03-2022
Similar questions
python - django modeladmin list_display
I'm trying to play with the Django's official tutorial. Specifically the modeladmin list_display:
http://docs.djangoproject.com/en/1.2/intro/tutorial02/#customize-the-admin-change-list
How can I add a column that displays the number of choices for each poll in the list?
Than...
python - Use Django list_display ModelAdmin in mainsite?
I've got a reasonably good AdminSite working for my app (very much a data-entry / dashboard-style app for scientists working in a collaborative team).
The users have a bunch of experiments they can sort, filter, edit, etc. This works well in the AdminSite.
It seems like the best practice (since I'd like to move to building more custom views/reports) is not to hack the AdminSite to bits ('the admin is not yo...
python - Django: how to including inline model fields in the list_display?
I'm attempting to extend django's contrib.auth User model, using an inline 'Profile' model to include extra fields.
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
class Profile(models.Model):
user = models.ForeignKey(User, unique=True, related_name='profile')
avatar = '/images/avatar.png'
nickname = 'Renz'
class UserProfi...
python - django modeladmin list_display
I'm trying to play with the Django's official tutorial. Specifically the modeladmin list_display:
http://docs.djangoproject.com/en/1.2/intro/tutorial02/#customize-the-admin-change-list
How can I add a column that displays the number of choices for each poll in the list?
Than...
javascript - Django - Replicating the admin list_display
What's the easiest method to get list_display functionality in my main website (outside of the admin pages).
I have a group of elements I'd like to select and perform an action. Any ideas?
python - Django admin list_display weirdly slow with foreign keys
Django 1.2.5
Python: 2.5.5
My admin list of a sports model has just gone really slow (5 minutes for 400 records). It was returning in a second or so until we got 400 games, 50 odd teams and 2 sports.
I have fixed it in an awful way so I'd like to see if anyone has seen this before. My app looks like this:
models:
Sport( models.Model )
name
Venue( models.Model )
name
Team( models.M...
python - Django admin list_display property usage
I have models and their admin code at below. The question is how can I show the first three tag of a book in its list_display property ?
I can show the tags while the book is editing but I would like to its 3 tags while the book are listed in the admin panel.
models.py
class Book(models.Model):
name = models.CharField(max_length=1000)
def __unicod...
python - My own method used in list_display and value as boolean icon
I wrote my own method used in list_display (admin class), like this:
class MyClassAdmin(admin.ModelAdmin):
list_display = ('my_own_method')
def my_own_method(self, obj):
if [condition]:
return True
else:
return False
but this value is displayed on list as text (True or False), not as default django boolean icons like this:
python - list_display with a function, how to pass arguments?
The Django admin docs says that it is possible to specify a callable as a value that can be used in list_display. If I need to pass some extra context to the function via function arguments, what's the best way to accomplish that?
In pseudo code, what I'd like to do is some...
python - Overriding list_display in Django admin with custom verbose name
I have overridden the list_display to show inline fields like this:
class opportunityAdmin(admin.ModelAdmin):
list_display = ('name', 'Contact', 'Phone', 'Address', 'discovery_date', 'status' , 'outcome')
search_fields = ['name', 'tags' , 'description']
#readonly_fields = ('discovery_date','close_date')
inlines = [account_contactInline, account_updateInline]
def Contact(self, obj):
...
python - DJANGO: How to list_display a reverse foreign key attribute?
I'm building a web app that tracks what library books a person checks out. I have the following models:
class Person(models.Model):
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class Book(models.Model):
name = models.CharField(max_length=100)
person = models.ForeignKey(Person)
checkout_date = models.DateTimeField('checkout date')
def __uni...
python - Django -- list_display has no effect
This is my code:
from django.contrib import admin
from bjorncsv.models import *
class BondsAdmin(admin.ModelAdmin):
list_display = ("rowid", "bond_id", "end_d", "intr", "base_i", "type", "start_d", "first_id", "first_pd")
admin.site.register(Bonds)
However, on the Admin interface, it's like the class isn't even there. This is the class in question:
class Bonds(models....
Still can't find your answer? Check out these communities...
PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python