问题:Django ModelAdmin中的“ list_display”可以显示ForeignKey字段的属性吗?
我有一个Person
模型,它与有一个外键关系Book
,该模型有许多字段,但我最关心的是author
(标准CharField)。
话虽如此,在我的PersonAdmin
模型中,我想book.author
使用显示list_display
:
class PersonAdmin(admin.ModelAdmin):
list_display = ['book.author',]
我已经尝试了所有显而易见的方法来执行此操作,但是似乎没有任何效果。
有什么建议么?
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?
回答 0
作为另一种选择,您可以进行如下查找:
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'
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'
回答 1
尽管上面有很多很棒的答案,但由于我是Django的新手,所以我仍然受困。这是我从一个新手的角度进行的解释。
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(不正确的方式) -您认为使用’model__field’进行引用可以正常工作,但它不起作用
class BookAdmin(admin.ModelAdmin):
model = Book
list_display = ['title', 'author__name', ]
admin.site.register(Book, BookAdmin)
admin.py(正确的方式) -这就是您以Django方式引用外键名称的方式
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)
有关其他参考,请参见此处的Django模型链接
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
回答 2
和其余的一样,我也使用可调用对象。但是它们有一个缺点:默认情况下,您无法订购它们。幸运的是,有一个解决方案:
的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'
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'
回答 3
请注意,添加该get_author
功能会减慢admin中的list_display速度,因为显示每个人都会进行SQL查询。
为了避免这种情况,您需要get_queryset
在PersonAdmin中修改方法,例如:
def get_queryset(self, request):
return super(PersonAdmin,self).get_queryset(request).select_related('book')
之前:36.02毫秒内有73个查询(管理员中有67个重复查询)
之后:10.81毫秒内进行6次查询
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')
Before: 73 queries in 36.02ms (67 duplicated queries in admin)
After: 6 queries in 10.81ms
回答 4
回答 5
我刚刚发布了一个片段,使admin.ModelAdmin支持’__’语法:
http://djangosnippets.org/snippets/2887/
因此,您可以执行以下操作:
class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]
基本上,这只是在做其他答案中描述的相同操作,但是它会自动处理(1)设置admin_order_field(2)设置short_description以及(3)修改查询集以避免每一行都有数据库命中。
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.
回答 6
您可以使用可调用对象在列表显示中显示所需的任何内容。它看起来像这样:
def book_author(object):
返回object.book.author
类PersonAdmin(admin.ModelAdmin):
list_display = [book_author,]
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,]
回答 7
这个已经被接受了,但是如果还有其他假人(像我一样)没有立即从当前接受的答案中得到答案,那么这里有更多细节。
ForeignKey
需要引用的模型类在其中需要一个__unicode__
方法,例如:
class Category(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
这对我来说很重要,应该适用于上述情况。这适用于Django 1.0.2。
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.
回答 8
如果您有很多关联属性字段供使用,list_display
并且不想为每个函数创建一个函数(及其属性),那么一个肮脏而简单的解决方案将覆盖ModelAdmin
instace __getattr__
方法,即刻创建可调用对象:
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
作为要点
可调用的特殊属性,例如boolean
和short_description
必须定义为ModelAdmin
属性,例如book__author_verbose_name = 'Author name'
和category__is_new_boolean = True
。
callable admin_order_field
属性是自动定义的。
不要忘记在您的列表中使用list_select_related属性,ModelAdmin
以使Django避免常规查询。
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.
回答 9
PyPI中有一个非常易于使用的软件包,可以准确地处理该软件包:django-related-admin。您还可以在GitHub中查看代码。
使用此功能,您想要实现的过程很简单:
class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]
这两个链接均包含安装和使用的完整详细信息,因此,如果它们发生更改,我就不会在此处粘贴它们。
顺便提一句,如果您已经使用了其他工具model.Admin
(例如,我正在使用SimpleHistoryAdmin
),则可以执行以下操作:class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin)
。
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)
.
回答 10
如果您在Inline中尝试,除非以下条件,否则您不会成功:
在您的内联中:
class AddInline(admin.TabularInline):
readonly_fields = ['localname',]
model = MyModel
fields = ('localname',)
在您的模型(MyModel)中:
class MyModel(models.Model):
localization = models.ForeignKey(Localizations)
def localname(self):
return self.localization.name
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
回答 11
AlexRobbins的答案对我有用,除了前两行需要在模型中(也许这是假设的?),并且应该引用self:
def book_author(self):
return self.book.author
然后管理部分可以很好地工作。
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.
回答 12
我更喜欢这样:
class CoolAdmin(admin.ModelAdmin):
list_display = ('pk', 'submodel__field')
@staticmethod
def submodel__field(obj):
return obj.submodel.field
I prefer this:
class CoolAdmin(admin.ModelAdmin):
list_display = ('pk', 'submodel__field')
@staticmethod
def submodel__field(obj):
return obj.submodel.field