J'ai un modèle Person
qui a une relation de clé étrangère avec Book
, qui comporte un certain nombre de champs, mais je m'inquiète surtout de author
(un CharField standard).
Cela dit, dans mon modèle PersonAdmin
, j'aimerais afficher book.author
à l'aide de list_display
:
class PersonAdmin(admin.ModelAdmin):
list_display = ['book.author',]
J'ai essayé toutes les méthodes évidentes pour le faire, mais rien ne semble fonctionner.
Aucune suggestion?
Une autre option consiste à effectuer des recherches comme:
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'
En dépit de toutes les bonnes réponses ci-dessus et du fait que je sois nouveau à Django, j'étais toujours bloqué. Voici mon explication d'un point de vue très novice.
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 (façon incorrecte) - vous pensez que cela fonctionnerait en utilisant 'model__field' pour faire référence, mais ce n'est pas le cas
class BookAdmin(admin.ModelAdmin):
model = Book
list_display = ['title', 'author__name', ]
admin.site.register(Book, BookAdmin)
admin.py (Correct Way) - C’est ainsi que vous faites référence à une clé étrangère nommée de la manière 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)
Pour une référence supplémentaire, voir le lien du modèle Django --- ici
Comme le reste, je suis allé avec callables aussi. Mais ils ont un inconvénient: par défaut, vous ne pouvez pas commander sur eux. Heureusement, il existe une solution pour cela:
def author(self):
return self.book.author
author.admin_order_field = 'book__author'
Veuillez noter que l'ajout de la fonction get_author
ralentirait l'affichage de la liste dans l'administrateur, car le fait d'afficher chaque personne effectuerait une requête SQL.
Pour éviter cela, vous devez modifier la méthode get_queryset
dans PersonAdmin, par exemple:
def get_queryset(self, request):
return super(PersonAdmin,self).get_queryset(request).select_related('book')
Avant: 73 requêtes en 36.02ms (67 requêtes dupliquées dans admin)
Après: 6 requêtes en 10.81ms
Selon la documentation, vous ne pouvez afficher que la représentation __unicode__
d'une ForeignKey:
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#list-display
Cela semble étrange de ne pas prendre en charge le format de style 'book__author'
utilisé partout ailleurs dans l'API de base de données.
Il s'avère qu'il y a n ticket pour cette fonctionnalité , qui est marqué comme ne résoudra pas.
Vous pouvez afficher ce que vous voulez dans l'affichage de liste en utilisant un appelable. Cela ressemblerait à ceci:
def book_author (objet): retourner object.book.author classe PersonAdmin (admin.ModelAdmin): list_display = [book_author,]
Je viens de publier un extrait qui rend la syntaxe '__' de support de admin.ModelAdmin:
http://djangosnippets.org/snippets/2887/
Alors tu peux faire:
class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]
En gros, il s’agit essentiellement de la même chose que celle décrite dans les autres réponses, mais cela prend automatiquement en charge (1) le paramétrage de admin_order_field (2) le paramétrage de short_description et (3) la modification du queryset afin d’éviter un hit de base de données pour chaque ligne.
Celui-ci est déjà accepté, mais s'il y a d'autres mannequins (comme moi) qui ne l'ont pas immédiatement compris par réponse actuellement acceptée , voici un peu plus de détails.
La classe de modèle référencée par la variable ForeignKey
doit contenir une méthode __unicode__
, comme ici:
class Category(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
Cela a fait la différence pour moi et devrait s’appliquer au scénario ci-dessus. Cela fonctionne sur Django 1.0.2.
Si vous avez beaucoup de champs d'attributs de relation à utiliser dans list_display
et que vous ne voulez pas créer de fonction (et d'attributs) pour chacun d'eux, une solution simple mais simple aurait priorité sur l'instance ModelAdmin
instace __getattr__
, créant les callables à la volée:
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
Comme Gist here
Les attributs spéciaux appelables tels que boolean
et short_description
doivent être définis comme étant les attributs ModelAdmin
, par exemple book__author_verbose_name = 'Author name'
et category__is_new_boolean = True
.
L'attribut callable admin_order_field
est défini automatiquement.
N'oubliez pas d'utiliser l'attribut list_select_related dans votre ModelAdmin
pour que Django évite les requêtes supplémentaires.
si vous l'essayez en ligne, vous ne réussirez que si:
dans votre inline:
class AddInline(admin.TabularInline):
readonly_fields = ['localname',]
model = MyModel
fields = ('localname',)
dans votre modèle (MyModel):
class MyModel(models.Model):
localization = models.ForeignKey(Localizations)
def localname(self):
return self.localization.name
PyPI contient un paquet très facile à utiliser qui gère exactement cela: Django-related-admin . Vous pouvez aussi voir le code dans GitHub .
En utilisant ceci, ce que vous voulez réaliser est aussi simple que:
class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]
Les deux liens contiennent des détails complets sur l'installation et l'utilisation. Par conséquent, je ne les collerai pas ici au cas où ils changeraient.
En guise de remarque, si vous utilisez déjà autre chose que model.Admin
(par exemple, j’utilisais plutôt SimpleHistoryAdmin
, vous pouvez le faire: class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin)
.