¿Puede “list_display” en un Django ModelAdmin mostrar los atributos de los campos de ForeignKey?

Tengo un modelo de Person que tiene una relación de clave externa con Book , que tiene varios campos, pero me preocupa más el author (un campo de caracteres estándar).

Dicho esto, en mi modelo PersonAdmin , me gustaría mostrar book.author utilizando list_display :

 class PersonAdmin(admin.ModelAdmin): list_display = ['book.author',] 

He intentado todos los métodos obvios para hacerlo, pero nada parece funcionar.

¿Alguna sugerencia?

    Como otra opción, puedes hacer búsquedas como:

     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' 

    A pesar de todas las grandes respuestas anteriores y debido a que soy nuevo en Django, todavía estaba atascado. Aquí está mi explicación desde una perspectiva muy novata.

    modelos.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 incorrecto) : cree que funcionaría utilizando ‘model__field’ como referencia, pero no funciona

     class BookAdmin(admin.ModelAdmin): model = Book list_display = ['title', 'author__name', ] admin.site.register(Book, BookAdmin) 

    admin.py (Modo correcto) : así es como usted hace referencia a un nombre de clave externa al estilo 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) 

    Para referencia adicional, vea el enlace del modelo Django aquí

    Como el rest, yo también fui con callables. Pero tienen un inconveniente: de forma predeterminada, no puede ordenarlos. Afortunadamente, hay una solución para eso:

     def author(self): return self.book.author author.admin_order_field = 'book__author' 

    Tenga en cuenta que agregar la función get_author ralentizaría el list_display en el administrador, porque mostrar a cada persona haría una consulta SQL.

    Para evitar esto, necesita modificar el método get_queryset en PersonAdmin, por ejemplo:

     def get_queryset(self, request): return super(PersonAdmin,self).get_queryset(request).select_related('book') 

    Antes: 73 consultas en 36.02ms (67 consultas duplicadas en admin)

    Después: 6 consultas en 10.81ms.

    De acuerdo con la documentación, solo puede mostrar la representación __unicode__ de una ForeignKey:

    http://docs.djangoproject.com/en/dev/ref/contrib/admin/#list-display

    Parece extraño que no admita el formato de estilo 'book__author' que se usa en cualquier otro lugar en la API de DB.

    Resulta que hay un ticket para esta función , que está marcado como No se solucionará.

    Puede mostrar lo que quiera en la visualización de la lista utilizando un llamador. Se vería así:

    
     def book_author (objeto):
       devolver object.book.author
    
     clase PersonAdmin (admin.ModelAdmin):
       list_display = [book_author,] 

    Acabo de publicar un fragmento de código que hace que admin.ModelAdmin admita la syntax ‘__’:

    http://djangosnippets.org/snippets/2887/

    Así que puedes hacer:

     class PersonAdmin(RelatedFieldAdmin): list_display = ['book__author',] 

    Básicamente, se trata de hacer lo mismo que se describe en las otras respuestas, pero se encarga automáticamente de (1) configurar admin_order_field (2) configurar short_description y (3) modificar el queryset para evitar un golpe de base de datos para cada fila.

    Este ya está aceptado, pero si hay otros tontos por ahí (como yo) que no obtuvieron de inmediato la respuesta aceptada , aquí hay un poco más de detalles.

    La clase modelo a la que hace referencia ForeignKey debe tener un método __unicode__ , como aquí:

     class Category(models.Model): name = models.CharField(max_length=50) def __unicode__(self): return self.name 

    Eso hizo la diferencia para mí, y debería aplicarse al escenario anterior. Esto funciona en Django 1.0.2.

    Si lo intentas en línea, no lo lograrás a menos que:

    en su línea:

     class AddInline(admin.TabularInline): readonly_fields = ['localname',] model = MyModel fields = ('localname',) 

    En tu modelo (MyModel):

     class MyModel(models.Model): localization = models.ForeignKey(Localizations) def localname(self): return self.localization.name 

    Si tiene una gran cantidad de campos de atributos de relación para usar en list_display y no desea crear una función (y sus atributos) para cada uno, una solución simple pero sencilla sería anular el método ModelAdmin instace __getattr__ , creando los callables sobre la marcha:

     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 

    Como gist aquí

    Los atributos especiales recuperables como boolean y short_description deben definirse como atributos ModelAdmin , por ejemplo, book__author_verbose_name = 'Author name' y category__is_new_boolean = True .

    El atributo admin_order_field se define automáticamente.

    No olvide usar el atributo list_select_related en su ModelAdmin para hacer que Django evite consultas adicionales.

    Hay un paquete muy fácil de usar disponible en PyPI que maneja exactamente eso: django-related-admin . También puedes ver el código en GitHub .

    Usando esto, lo que quieres lograr es tan simple como:

     class PersonAdmin(RelatedFieldAdmin): list_display = ['book__author',] 

    Ambos enlaces contienen todos los detalles de instalación y uso, por lo que no los pegaré aquí en caso de que cambien.

    Solo como nota al margen, si ya está usando algo que no sea model.Admin (por ejemplo, yo estaba usando SimpleHistoryAdmin en SimpleHistoryAdmin lugar), puede hacer esto: class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin) .

    La respuesta de AlexRobbins funcionó para mí, excepto que las dos primeras líneas deben estar en el modelo (¿quizás esto fue asumido?), Y deberían referirse a sí mismo:

     def book_author(self): return self.book.author 

    Entonces la parte de administrador funciona muy bien.

    Prefiero esto:

     class CoolAdmin(admin.ModelAdmin): list_display = ('pk', 'submodel__field') @staticmethod def submodel__field(obj): return obj.submodel.field