wake-up-neo.com

Kann "list_display" in einem Django ModelAdmin Attribute von ForeignKey-Feldern anzeigen?

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?

262
Huuuze

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'
432
imjoevasquez

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

122
Will

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'
61
Arjen

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

36
Hunger

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.

20
Jonny Buchanan

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,]
10
AlexRobbins

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.

10
Jack Cushman

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.

6
bmelton

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.

4
Cauê Thenório

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
4
Eyal Ch

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).

3