Pregunta

Tengo un modelo de Person que tiene una relación de clave externa con Book , que tiene varios campos, pero estoy más preocupado por autor (un CharField estándar).

Dicho esto, en mi modelo PersonAdmin , me gustaría mostrar book.author usando 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?

¿Fue útil?

Solución

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'

Otros consejos

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

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) : crees que funcionaría usando 'model__field' como referencia, pero no lo hace

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

admin.site.register(Book, BookAdmin)

admin.py (Forma correcta) : así es como se hace referencia a la clave externa que se denomina 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 de Django aquí

Como el resto, 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, debe 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

Según 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 sea compatible con el formato de estilo 'book__author' que se usa en cualquier otra parte de 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 usando un llamador. Se vería así:

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

class PersonAdmin(admin.ModelAdmin):
  list_display = [book_author,]

Acabo de publicar un fragmento de código que hace que admin.ModelAdmin sea compatible con la sintaxis '__':

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 se ha aceptado, pero si hay otros dummies (como yo) que no lo obtuvieron de inmediato desde respuesta aceptada actualmente , aquí hay un poco más de detalles.

La clase modelo a la que hace referencia 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 marcó 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 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 simple pero sencilla reemplazará al 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 here

Los atributos especiales

recuperables como boolean y short_description deben definirse como atributos ModelAdmin , por ejemplo, book__author_verbose_name = 'Nombre del autor' y category__is_new_boolean = True .

El atributo admin_order_field llamable se define automáticamente.

No te olvides de usar < 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- relacionado-admin . También puede 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 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, estaba usando SimpleHistoryAdmin ), puede hacer esto: < código> clase 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ía hacer referencia a uno mismo:

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

Entonces la parte de administración funciona bien.

Prefiero esto:

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

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top