muitos-para-muitos no django de exibição de lista


94
class PurchaseOrder(models.Model):
    product = models.ManyToManyField('Product')
    vendor = models.ForeignKey('VendorProfile')
    dollar_amount = models.FloatField(verbose_name='Price')


class Product(models.Model):
   products = models.CharField(max_length=256)

   def __unicode__(self):
       return self.products

Eu tenho esse código. Infelizmente, o erro vem em admin.py com oManyToManyField

class PurchaseOrderAdmin(admin.ModelAdmin):
    fields = ['product', 'dollar_amount']
    list_display = ('product', 'vendor')

O erro diz:

'PurchaseOrderAdmin.list_display [0]', 'product' é um ManyToManyField que não é compatível.

No entanto, ele compila quando eu tomar 'product'fora do list_display. Então, como posso exibir 'product'no list_displaysem dar-lhe erros?

editar : Talvez a melhor pergunta seja como você exibe um ManyToManyFieldem list_display?

Respostas:


175

Você pode não conseguir fazer isso diretamente. Da documentação delist_display

Os campos ManyToManyField não são suportados, porque isso implicaria na execução de uma instrução SQL separada para cada linha da tabela. Se você quiser fazer isso mesmo assim, dê ao seu modelo um método personalizado e adicione o nome desse método a list_display. (Veja abaixo mais sobre métodos personalizados em list_display.)

Você pode fazer algo assim:

class PurchaseOrderAdmin(admin.ModelAdmin):
    fields = ['product', 'dollar_amount']
    list_display = ('get_products', 'vendor')

    def get_products(self, obj):
        return "\n".join([p.products for p in obj.product.all()])

OU defina um método de modelo e use-o

class PurchaseOrder(models.Model):
    product = models.ManyToManyField('Product')
    vendor = models.ForeignKey('VendorProfile')
    dollar_amount = models.FloatField(verbose_name='Price')

    def get_products(self):
        return "\n".join([p.products for p in self.product.all()])

e no admin list_display

list_display = ('get_products', 'vendor')

Esta parece ser uma solução muito boa. Obrigado. Embora, agora estou recebendo um erro dizendo que "valor nulo na coluna" product_id "viola a restrição não nula" Alguma ideia do que isso significa?
Mdjon26

3
Já que isso deixa o banco de dados de joelhos, como você faria isso com select_related () ou prefetch_related () para melhorar o desempenho?
Cloud Artisans

3
Caso a questão de otimização ainda seja interessante, acabei de ter o mesmo problema e descobri que você poderia simplesmente implementar um get_queryset()método otimizado para seu ModelAdmin, consulte stackoverflow.com/questions/12354099/…
goetz

1
@ SebastiánVansteenkiste Boa ideia, talvez cached_propertyajude. Mas acho que provavelmente não. Quando você usa um otimizado get_queryset, você pode, por exemplo, anotar / pré-processar os dados lá, como fazer a concatenação de produtos em SQL em vez de em Django, e armazenar seus dados personalizados em seu queryset. Então, você só precisaria executar essa lógica uma vez no SQL e não para cada linha quando a propriedade for acessada.
goetz

1
Bom ponto. Eu deveria get_querysettentar fazer o meu próprio otimizado , só não consegui ler os documentos completos (nem encontrei um exemplo simples o suficiente do que deveria estar fazendo)
Sebastián Vansteenkiste

16

Dessa forma, você pode fazer isso, por favor, verifique o seguinte snippet:

class Categories(models.Model):
    """ Base category model class """

    title       = models.CharField(max_length=100)
    description = models.TextField()
    parent      = models.ManyToManyField('self', default=None, blank=True)
    when        = models.DateTimeField('date created', auto_now_add=True)

    def get_parents(self):
        return ",".join([str(p) for p in self.parent.all()])

    def __unicode__(self):
        return "{0}".format(self.title)

E em seu método de chamada do módulo admin.py da seguinte maneira:

class categories(admin.ModelAdmin):
    list_display    = ('title', 'get_parents', 'when')
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.