molti-a-molti nella visualizzazione dell'elenco django


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

Ho quel codice. Sfortunatamente, l'errore arriva in admin.py con l'estensioneManyToManyField

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

L'errore dice:

"PurchaseOrderAdmin.list_display [0]", "product" è un ManyToManyField che non è supportato.

Tuttavia, si compila quando estendo 'product'da list_display. Così come posso visualizzare 'product'in list_displaysenza dare errori?

modifica : forse una domanda migliore sarebbe come visualizzare un ManyToManyFieldin list_display?

Risposte:


175

Potresti non essere in grado di farlo direttamente. Dalla documentazione dilist_display

I campi ManyToManyField non sono supportati, perché ciò comporterebbe l'esecuzione di un'istruzione SQL separata per ogni riga della tabella. Se vuoi farlo comunque, dai al tuo modello un metodo personalizzato e aggiungi il nome di quel metodo a list_display. (Vedi sotto per ulteriori informazioni sui metodi personalizzati in list_display.)

Puoi fare qualcosa del genere:

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()])

OPPURE definire un metodo modello e usarlo

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 nell'amministratore list_display

list_display = ('get_products', 'vendor')

Questa sembra davvero una buona soluzione. Grazie. Anche se ora ricevo un errore dicendo che "il valore nullo nella colonna" product_id "viola il vincolo non nullo" Qualche idea di cosa significhi?
Mdjon26

3
Dal momento che questo mette in ginocchio il database, come lo faresti con select_related () o prefetch_related () per migliorare le prestazioni?
Cloud Artisans

3
Nel caso in cui la questione di ottimizzazione è comunque interessante, ho appena avuto lo stesso problema e ha scoperto che si può semplicemente implementare un ottimizzato get_queryset()metodo per il ModelAdmin, vedere stackoverflow.com/questions/12354099/...
Goetz

1
@ SebastiánVansteenkiste Buona idea, forse cached_propertyaiuterebbe. Ma penso che probabilmente non lo sarebbe. Quando si utilizza un ottimizzato get_queryset, è possibile ad esempio annotare / preelaborare i dati lì, come eseguire la concatenazione di prodotti in SQL anziché in Django e memorizzare i dati personalizzati nel set di query. Quindi sarà necessario eseguire quella logica solo una volta in SQL e non per ogni riga quando si accede alla proprietà.
goetz

1
Buon punto. Dovrei cercare di fare il mio ottimizzato get_queryset, semplicemente non sono riuscito a leggere i documenti completi (né ho trovato un esempio abbastanza semplice di quello che dovrei fare)
Sebastián Vansteenkiste

16

In questo modo puoi farlo, controlla il seguente frammento di codice:

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 nel tuo metodo di chiamata del modulo admin.py come segue:

class categories(admin.ModelAdmin):
    list_display    = ('title', 'get_parents', 'when')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.