Pergunta

eu tenho um Person modelo que tem um relacionamento de chave estrangeira com Book, que tem vários campos, mas estou mais preocupado com author (um CharField padrão).

Dito isto, na minha PersonAdmin modelo, gostaria de exibir book.author usando list_display:

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

Tentei todos os métodos óbvios para fazer isso, mas nada parece funcionar.

Alguma sugestão?

Foi útil?

Solução

Como outra opção, você pode procurar ups 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'

Outras dicas

Apesar de todas as ótimas respostas acima e por ser novo no Django, eu ainda estava preso. Aqui está minha explicação de uma perspectiva muito 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 (maneira incorreta) - Você acha que funcionaria usando 'Model__field' para fazer referência, mas não

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

admin.site.register(Book, BookAdmin)

admin.py (maneira correta) - É assim que você faz referência a um nome de chave estrangeiro da maneira 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 referência adicional, consulte o link do modelo Django aqui

Como o resto, eu também fui com callables. Mas eles têm uma desvantagem: por padrão, você não pode pedir neles. Felizmente, há uma solução para isso:

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

Observe que adicionar o get_author A função retardaria o list_display no administrador, porque mostrar que cada pessoa faria uma consulta SQL.

Para evitar isso, você precisa modificar get_queryset Método em Personadmin, por exemplo:

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

Antes: 73 consultas em 36,02ms (67 consultas duplicadas em admin)

Depois: 6 consultas em 10,81ms

De acordo com a documentação, você só pode exibir o __unicode__ Representação de um ForeignKey:

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

Parece estranho que ele não suporta o 'book__author' formato de estilo que é usado em qualquer outro lugar da API de banco de dados.

Acontece que lá está um ingresso para este recurso, que é marcado como não corrigirá.

Você pode mostrar o que quiser no visor da lista usando um chamável. Seria assim:

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

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

Acabei de publicar um trecho que faz do admin.modeladmin suportar '__' sintaxe:

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

Então você pode fazer:

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

Isso é basicamente apenas fazendo a mesma coisa descrita nas outras respostas, mas atende automaticamente (1) configurando admin_order_field (2) Configurando Short_description e (3) modificando o consulta para evitar um acerto de banco de dados para cada linha.

Este já foi aceito, mas se houver outros manequins por aí (como eu), que não o tirou imediatamente do resposta atualmente aceita, aqui está um pouco mais de detalhe.

A classe modelo referenciada pelo ForeignKey precisa ter um __unicode__ Método dentro dele, como aqui:

class Category(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

Isso fez a diferença para mim e deve se aplicar ao cenário acima. Isso funciona no Django 1.0.2.

Se você tentar em linha, você não terá sucesso a menos que:

em sua inline:

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

em seu modelo (mymodel):

class MyModel(models.Model):
    localization = models.ForeignKey(Localizations)

    def localname(self):
        return self.localization.name

Se você tiver muitos campos de atributos de relacionamento para usar em list_display e não quiser criar uma função (e seus atributos) para cada um, uma solução suja, mas simples, seria substituir o ModelAdmin instância __getattr__ método, criando os callables dinamicamente:

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 essência aqui

Atributos especiais chamáveis ​​como boolean e short_description deve ser definido como ModelAdmin atributos, por exemplo book__author_verbose_name = 'Author name' e category__is_new_boolean = True.

O chamável admin_order_field atributo é definido automaticamente.

Não se esqueça de usar o list_select_relacionado atributo em seu ModelAdmin para fazer o Django evitar consultas adicionais.

Há um pacote muito fácil de usar disponível no Pypi que lida exatamente com isso: Django-Admin. Você também pode Veja o código no github.

Usando isso, o que você deseja alcançar é tão simples quanto:

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

Ambos os links contêm detalhes completos de instalação e uso para que não os coloque aqui, caso eles mudem.

Assim como uma nota lateral, se você já está usando algo diferente model.Admin (por exemplo, eu estava usando SimpleHistoryAdmin em vez disso), você pode fazer isso: class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin).

A resposta de Alexrobbins funcionou para mim, exceto que as duas primeiras linhas precisam estar no modelo (talvez isso tenha sido assumido?), E deve fazer referência a si mesmo:

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

Então a parte do administrador funciona bem.

Eu prefiro isso:

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

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top