“List_display” in un ModelAdmin di Django può visualizzare gli attributi dei campi ForeignKey?
-
03-07-2019 - |
Domanda
Ho un modello Person
che ha una relazione di chiave esterna con Book
, che ha un numero di campi, ma sono molto preoccupato per autore
(un CharField standard).
Detto questo, nel mio modello PersonAdmin
, vorrei visualizzare book.author
utilizzando list_display
:
class PersonAdmin(admin.ModelAdmin):
list_display = ['book.author',]
Ho provato tutti i metodi ovvi per farlo, ma nulla sembra funzionare.
Qualche suggerimento?
Soluzione
Come altra opzione, puoi fare delle ricerche come:
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'
Altri suggerimenti
Nonostante tutte le grandi risposte di cui sopra e perché ero nuovo di Django, ero ancora bloccato. Ecco la mia spiegazione da una prospettiva molto nuova.
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 (Modo errato) - pensi che funzionerebbe usando 'model__field' come riferimento, ma non
class BookAdmin(admin.ModelAdmin):
model = Book
list_display = ['title', 'author__name', ]
admin.site.register(Book, BookAdmin)
admin.py (Modo corretto) : ecco come fai riferimento a un nome di chiave esterna nel modo 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)
Per ulteriori riferimenti, consultare il collegamento al modello Django qui
Come il resto, sono andato anche con i callable. Ma hanno un aspetto negativo: per impostazione predefinita, non puoi ordinarli. Fortunatamente, esiste una soluzione per questo:
def author(self):
return self.book.author
author.admin_order_field = 'book__author'
Si noti che l'aggiunta della funzione get_author
rallenterebbe la visualizzazione di listino nell'amministratore, perché mostrare ad ogni persona farebbe una query SQL.
Per evitare ciò, è necessario modificare il metodo get_queryset
in PersonAdmin, ad esempio:
def get_queryset(self, request):
return super(PersonAdmin,self).get_queryset(request).select_related('book')
Prima: 73 query in 36.02ms (67 query duplicate in admin)
Dopo: 6 query in 10,81 ms
Secondo la documentazione, è possibile visualizzare solo la rappresentazione __unicode__
di un ForeignKey:
http://docs.djangoproject.com/en / dev / ref / contrib / admin / # lista-display
Sembra strano che non supporti il ??formato di stile 'book__author'
che viene usato ovunque nell'API DB.
Si scopre che un biglietto per questa funzione , contrassegnato come Non risolto.
Puoi mostrare quello che vuoi nella visualizzazione elenco usando un callable. Sembrerebbe così:
def book_author(object): return object.book.author class PersonAdmin(admin.ModelAdmin): list_display = [book_author,]
Ho appena pubblicato uno snippet che rende admin.ModelAdmin che supporta la sintassi '__':
http://djangosnippets.org/snippets/2887/
Quindi puoi fare:
class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]
Questo in pratica sta facendo la stessa cosa descritta nelle altre risposte, ma si occupa automaticamente di (1) impostazione admin_order_field (2) impostazione short_description e (3) modifica del queryset per evitare un hit del database per ogni riga.
/ p>
Questo è già accettato, ma se ci sono altri manichini là fuori (come me) che non lo hanno immediatamente ricevuto dal risposta attualmente accettata , ecco qualche dettaglio in più.
La classe del modello a cui fa riferimento il ForeignKey
deve contenere un metodo __unicode__
, come qui:
class Category(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
Ciò ha fatto la differenza per me e dovrebbe applicarsi allo scenario sopra. Funziona su Django 1.0.2.
se lo provi in ??Inline, non riuscirai a meno che:
nel tuo inline:
class AddInline(admin.TabularInline):
readonly_fields = ['localname',]
model = MyModel
fields = ('localname',)
nel tuo modello (MyModel):
class MyModel(models.Model):
localization = models.ForeignKey(Localizations)
def localname(self):
return self.localization.name
Se hai molti campi degli attributi di relazione da usare in list_display
e non vuoi creare una funzione (ed i suoi attributi) per ognuno, una soluzione sporca ma semplice avrebbe la precedenza sul __getattr__
, creando al volo i callable:
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
Come gist qui
Gli attributi speciali richiamabili come booleano
e short_description
devono essere definiti come attributi ModelAdmin
, ad es. book__author_verbose_name = 'Nome autore' e
category__is_new_boolean = True
.
L'attributo admin_order_field
richiamabile viene definito automaticamente.
Non dimenticare di usare list_select_related nell'attributo ModelAdmin
per evitare che Django eviti le query adizionali.
Esiste un pacchetto molto facile da usare disponibile in PyPI che gestisce esattamente questo: django- correlate amministratore . Puoi anche vedere il codice in GitHub .
Usando questo, ciò che vuoi ottenere è semplice come:
class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]
Entrambi i collegamenti contengono tutti i dettagli dell'installazione e dell'utilizzo, quindi non li incollerò qui in caso di modifiche.
Proprio come una nota a margine, se stai già utilizzando qualcosa di diverso da model.Admin
(ad es. stavo usando invece SimpleHistoryAdmin
), puoi farlo: < code> class MyAdmin (SimpleHistoryAdmin, RelatedFieldAdmin) .
La risposta di AlexRobbins ha funzionato per me, tranne per il fatto che le prime due righe devono essere nel modello (forse è stato ipotizzato?), e dovrebbe fare riferimento a sé:
def book_author(self):
return self.book.author
Quindi la parte admin funziona bene.
Preferisco questo:
class CoolAdmin(admin.ModelAdmin):
list_display = ('pk', 'submodel__field')
@staticmethod
def submodel__field(obj):
return obj.submodel.field