Puede "list display" en un Django ModelAdmin mostrar atributos de campos ForeignKey?


Tengo un modelo de Persona que tiene una relación de clave foránea con Book. Libro tiene una serie de campos, pero estoy más preocupado por "autor" (un CharField estándar).

Dicho esto, en mi modelo PersonAdmin, me gustaría mostrar "libro.autor "usando " list_display". He probado todos los métodos obvios para hacerlo (ver más abajo), pero nada parece funcionar. Alguna sugerencia?

class PersonAdmin(admin.ModelAdmin):
  list_display = ['book.author',]
Author: ChillarAnand, 2008-10-02

13 answers

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'
 359
Author: imjoevasquez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-25 16:10:06

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.

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 (Incorrect Way) - crees que funcionaría usando 'model _ _ field' para hacer referencia, pero no

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

admin.site.register(Book, BookAdmin)

Admin.py (Correct Way) - así es como se hace referencia a un nombre de clave foránea de la manera 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 obtener más información, consulte el enlace del modelo Django aquí

 92
Author: Will,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-04 12:02:50

Al igual que el resto, fui con callables también. Pero tienen un inconveniente: por defecto, no se puede ordenar en ellos. Afortunadamente, hay una solución para eso:

def author(self):
    return self.book.author
author.admin_order_field  = 'book__author'
 57
Author: Arjen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-03 07:41:01

Tenga en cuenta que agregar la función get_author ralentizaría 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.02 ms (67 consultas duplicadas en admin)

Después de: 6 consultas en 10.81 ms

 24
Author: Hunger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-30 08:05:15

De acuerdo con la documentación, solo se 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 soporte el formato de estilo 'book__author' que se usa en todas partes en la API de la base de datos.

Resulta que hay un ticket para esta característica, que está marcado como No solucionado.

 19
Author: Jonny Buchanan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-12 02:45:02

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


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

class PersonAdmin(admin.ModelAdmin):
  list_display = [book_author,]
 10
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-30 17:41:43

Acabo de publicar un fragmento que hace admin.Sintaxis de ModelAdmin support'__':

Http://djangosnippets.org/snippets/2887 /

Así que puedes hacer:

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

Esto es básicamente hacer lo mismo que se describe en las otras respuestas, pero automáticamente se encarga de (1) establecer admin_order_field (2) establecer short_description y (3) modificar el conjunto de consultas para evitar un golpe en la base de datos para cada fila.

 9
Author: Jack Cushman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-03 21:21:58

Este ya está aceptado, pero si hay otros maniquíes por ahí (como yo) que no lo obtuvieron inmediatamente de la respuesta actualmente aceptada, aquí hay un poco más de detalle.

La clase modelo referenciada por el ForeignKey necesita tener un método __unicode__ dentro de ella, 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.

 5
Author: Community,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:14

Si lo intentas en línea, no tendrás éxito 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
 3
Author: Eyal Ch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-30 12:30:33

Si tiene muchos 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 sucia pero simple 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 lo esencial aquí

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

El atributo callable admin_order_field está definido automática.

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

 3
Author: Cauê Thenório,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-12 03:42:30

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 detalles completos de la instalación y el uso, por lo que no los pegaré aquí en caso de que cambien.

Como nota al margen, si ya estás usando algo que no sea model.Admin (por ejemplo, estaba usando SimpleHistoryAdmin en su lugar), puedes hacer esto: class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin).

 3
Author: Vlad Schnakovszki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-28 10:30:55

La respuesta de AlexRobbins funcionó para mí, excepto que las dos primeras líneas deben estar en el modelo (¿quizás se asumió esto?), y debe hacer referencia a sí mismo:

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

Entonces la parte de administración funciona muy bien.

 -1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-25 00:27:08

Prefiero esto:

class CoolAdmin(admin.ModelAdmin):
    list_display = ('pk', 'submodel__field')

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field
 -3
Author: wieczorek1990,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-22 14:48:41