muchos a muchos en la visualización de la lista django

4 minutos de lectura

avatar de usuario
mdjon26

class Product(models.Model):
    products = models.CharField(max_length=256)
    
    def __unicode__(self):
        return self.products

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

yo tengo ese codigo Desafortunadamente, el error viene en admin.py con el ManyToManyField

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

El error dice:

‘OrderAdmin.list_display[0]’, ‘producto’ es un ManyToManyField que no es compatible.

Sin embargo, se compila cuando tomo 'product' fuera de list_display. Entonces, ¿cómo puedo mostrar 'product' en list_display sin dar errores?

editar: Tal vez una mejor pregunta sería ¿cómo se muestra un ManyToManyField en list_display?

avatar de usuario
Karthikr

Es posible que no pueda hacerlo directamente. De la documentación de list_display

Los campos ManyToManyField no son compatibles, porque eso implicaría ejecutar una instrucción SQL separada para cada fila de la tabla. No obstante, si desea hacer esto, asigne a su modelo un método personalizado y agregue el nombre de ese método a list_display. (Consulte a continuación para obtener más información sobre los métodos personalizados en list_display).

Puedes hacer algo como esto:

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

O definir un método de modelo y utilizarlo

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

y en el administrador list_display

list_display = ('get_products', 'vendor')

  • Esto parece una muy buena solución. Gracias. Aunque ahora recibo un error que dice “valor nulo en la columna” product_id “viola la restricción no nula” ¿Alguna idea de lo que esto significa?

    – Mdjon26

    07/08/2013 a las 16:34


  • Dado que esto pone de rodillas a la base de datos, ¿cómo lo haría con select_related() o prefetch_related() para mejorar el rendimiento?

    – Artesanos de la nube

    14 mayo 2017 a las 23:39

  • En caso de que la pregunta de optimización siga siendo interesante, acabo de tener el mismo problema y descubrí que simplemente podría implementar un optimizado get_queryset() método para su ModelAdminconsulte stackoverflow.com/questions/12354099/…

    – goetz

    11 de julio de 2017 a las 13:45


  • @SebastiánVansteenkiste Buena idea, quizás cached_property ayudaría. Pero creo que probablemente no lo sería. Cuando utiliza un optimizado get_queryset, podría, por ejemplo, anotar/procesar previamente los datos allí, como hacer la concatenación de productos en SQL en lugar de en Django, y almacenar sus datos personalizados en su conjunto de consultas. Entonces solo necesitaría ejecutar esa lógica una vez en SQL y no para cada fila cuando se accede a la propiedad.

    – goetz

    8 de agosto de 2019 a las 16:55

  • Buen punto. Debería considerar hacer mi propio optimizado get_querysetsimplemente no pude leer los documentos completos (ni encontré un ejemplo lo suficientemente simple de lo que debería estar haciendo)

    – Sebastián Vansteenkiste

    8 de agosto de 2019 a las 17:07

De esta manera puede hacerlo, por favor revise el siguiente fragmento:

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)

Y en su método de llamada de módulo admin.py de la siguiente manera:

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

avatar de usuario
lucas b

Si desea ahorrar consultas adicionales, puede utilizar prefetch_related en el get_queryset método como a continuación:

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

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        return qs.prefetch_related('product')

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

De acuerdo con la DocumentosDe esta manera, solo se necesitaría una consulta adicional para obtener Product artículos de todos PurchaseOrder instancias en lugar de necesitar una consulta por cada PurchaseOrder instancia.

¿Ha sido útil esta solución?