Ich habe ein Person
Modell, das eine Fremdschlüsselbeziehung zu Book
hat, die eine Reihe von Feldern enthält, aber ich mache mir am meisten Sorgen um author
(ein Standard-CharField).
Nachdem dies gesagt wurde, möchte ich in meinem PersonAdmin
Modell book.author
mit list_display
:
class PersonAdmin(admin.ModelAdmin):
list_display = ['book.author',]
Ich habe alle offensichtlichen Methoden ausprobiert, aber nichts scheint zu funktionieren.
Irgendwelche Vorschläge?
Als weitere Option können Sie Folgendes nachschlagen:
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'
Trotz all der großartigen Antworten oben und weil ich neu bei Django war, steckte ich immer noch fest. Hier ist meine Erklärung aus einer sehr neuen Perspektive.
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 (Inkorrekter Weg) - Sie denken, es würde funktionieren, wenn Sie mit 'model__field' referenzieren, aber nicht
class BookAdmin(admin.ModelAdmin):
model = Book
list_display = ['title', 'author__name', ]
admin.site.register(Book, BookAdmin)
admin.py (Correct Way) - auf diese Weise verweisen Sie auf einen Fremdschlüsselnamen auf die Weise 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)
Weitere Informationen finden Sie unter Django Modelllink hier
Wie die anderen bin auch ich mit Callables gefahren. Aber sie haben einen Nachteil: Standardmäßig können Sie nicht auf sie bestellen. Zum Glück gibt es dafür eine Lösung:
def author(self):
return self.book.author
author.admin_order_field = 'book__author'
Beachten Sie, dass das Hinzufügen der Funktion get_author
Die Anzeige von list_display im Administrator verlangsamt, da beim Anzeigen jeder Person eine SQL-Abfrage ausgeführt wird.
Um dies zu vermeiden, müssen Sie die Methode get_queryset
In PersonAdmin ändern, zum Beispiel:
def get_queryset(self, request):
return super(PersonAdmin,self).get_queryset(request).select_related('book')
Vorher: 73 Abfragen in 36.02ms (67 doppelte Abfragen in admin)
Nachher: 6 Abfragen in 10,81 ms
Laut Dokumentation können Sie nur die __unicode__
- Darstellung eines ForeignKey anzeigen:
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#list-display
Scheint seltsam, dass es das Format 'book__author'
Nicht unterstützt, das sonst überall in der DB-API verwendet wird.
Es stellte sich heraus, dass es ein Ticket für dieses Feature gibt, das als "Won't Fix" markiert ist.
Sie können in der Listendarstellung alles anzeigen, was Sie möchten, indem Sie eine aufrufbare Funktion verwenden. Es würde so aussehen:
def book_author (object): return object.book.author class PersonAdmin (admin.ModelAdmin): list_display = [Buchautor,]
Ich habe gerade ein Snippet gepostet, mit dem admin.ModelAdmin die '__'-Syntax unterstützt:
http://djangosnippets.org/snippets/2887/
Sie können also Folgendes tun:
class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]
Dies geschieht im Grunde genommen genauso wie in den anderen Antworten beschrieben, es wird jedoch automatisch dafür gesorgt, dass (1) admin_order_field festgelegt wird, (2) short_description festgelegt wird und (3) der Abfragesatz geändert wird, um einen Datenbanktreffer für jede Zeile zu vermeiden.
Dies ist bereits akzeptiert, aber wenn es andere Dummies gibt (wie ich), die es nicht sofort aus der derzeit akzeptierte Antwort bekommen haben, hier ein bisschen mehr Details.
Die Modellklasse, auf die durch ForeignKey
verwiesen wird, muss ein __unicode__
Methode darin, wie hier:
class Category(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
Das machte den Unterschied für mich und sollte für das obige Szenario gelten. Dies funktioniert auf Django 1.0.2.
Wenn Sie viele Beziehungsattributfelder in list_display
Verwenden und nicht für jedes eine Funktion (und deren Attribute) erstellen möchten, würde eine schmutzige, aber einfache Lösung die Instanz ModelAdmin
überschreiben __getattr__
- Methode, die Callables im laufenden Betrieb erstellt:
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 hier
Aufrufbare Sonderattribute wie boolean
und short_description
Müssen als ModelAdmin
-Attribute definiert werden, z. B. book__author_verbose_name = 'Author name'
Und category__is_new_boolean = True
.
Das aufrufbare Attribut admin_order_field
Wird automatisch definiert.
Vergessen Sie nicht, das Attribut list_select_related in Ihrem ModelAdmin
zu verwenden, um Django) zusätzliche Abfragen zu vermeiden.
wenn Sie es in Inline versuchen, wird es Ihnen nicht gelingen, es sei denn:
in Ihrer Inline:
class AddInline(admin.TabularInline):
readonly_fields = ['localname',]
model = MyModel
fields = ('localname',)
in Ihrem Modell (MyModel):
class MyModel(models.Model):
localization = models.ForeignKey(Localizations)
def localname(self):
return self.localization.name
In PyPI ist ein sehr einfach zu verwendendes Paket verfügbar, das genau das erledigt: Django-related-admin . Sie können auch siehe den Code in GitHub .
Was Sie damit erreichen wollen, ist so einfach wie:
class PersonAdmin(RelatedFieldAdmin):
list_display = ['book__author',]
Beide Links enthalten alle Details zur Installation und Verwendung, sodass ich sie hier nicht einfügen werde, falls sie sich ändern.
Nur als Randnotiz, wenn Sie bereits etwas anderes als model.Admin
Verwenden (z. B. habe ich stattdessen SimpleHistoryAdmin
verwendet), können Sie dies tun: class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin)
.