affichage plusieurs à plusieurs dans la liste django

91
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

J'ai ce code. Malheureusement, l'erreur vient dans admin.py avec leManyToManyField

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

L'erreur dit:

'PurchaseOrderAdmin.list_display [0]', 'product' est un ManyToManyField qui n'est pas pris en charge.

Cependant, il se compile lorsque je 'product'sors de list_display. Alors , comment puis - je afficher 'product'en list_displaysans lui donner des erreurs?

edit : Peut-être qu'une meilleure question serait de savoir comment afficher un ManyToManyFieldin list_display?

Mdjon26
la source

Réponses:

171

Vous ne pourrez peut-être pas le faire directement. De la documentation delist_display

Les champs ManyToManyField ne sont pas pris en charge, car cela impliquerait l'exécution d'une instruction SQL distincte pour chaque ligne de la table. Si vous souhaitez néanmoins le faire, attribuez à votre modèle une méthode personnalisée et ajoutez le nom de cette méthode à list_display. (Voir ci-dessous pour plus d'informations sur les méthodes personnalisées dans list_display.)

Vous pouvez faire quelque chose comme ceci:

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 définissez une méthode modèle et utilisez-la

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

et dans l'admin list_display

list_display = ('get_products', 'vendor')
karthikr
la source
Cela semble être une très bonne solution. Merci. Bien que j'obtienne maintenant une erreur disant "valeur nulle dans la colonne" id_produit "viole la contrainte non nulle" Une idée de ce que cela signifie?
Mdjon26
3
Puisque cela met la base de données à genoux, comment le feriez-vous avec select_related () ou prefetch_related () pour améliorer les performances?
Cloud Artisans
3
Juste au cas où la question d'optimisation serait toujours intéressante, j'ai juste eu le même problème et j'ai découvert que vous pouviez simplement implémenter une get_queryset()méthode optimisée pour votre ModelAdmin, voir stackoverflow.com/questions/12354099
...
1
@ SebastiánVansteenkiste Bonne idée, peut cached_property- être aiderait. Mais je pense que ce ne serait probablement pas le cas. Lorsque vous utilisez un optimisé get_queryset, vous pouvez par exemple y annoter / pré-traiter les données, comme faire la concaténation des produits en SQL au lieu de Django, et stocker vos données personnalisées dans votre jeu de requêtes. Ensuite, vous n'aurez besoin d'exécuter cette logique qu'une seule fois dans SQL et non pour chaque ligne lors de l'accès à la propriété.
goetz
1
Bon point. Je devrais chercher à faire mon propre optimisé get_queryset, je n'ai tout simplement pas eu le temps de lire la documentation complète (ni trouvé un exemple assez simple de ce que je devrais faire)
Sebastián Vansteenkiste
16

De cette façon, vous pouvez le faire, veuillez consulter l'extrait de code suivant:

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)

Et dans votre méthode d'appel de module admin.py comme suit:

class categories(admin.ModelAdmin):
    list_display    = ('title', 'get_parents', 'when')
JKV
la source