many-to-many in list display django

Mdjon26 picture Mdjon26 · Aug 7, 2013 · Viewed 53.9k times · Source
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

I have that code. Unfortunately, the error comes in admin.py with the ManyToManyField

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

The error says:

'PurchaseOrderAdmin.list_display[0]', 'product' is a ManyToManyField which is not supported.

However, it compiles when I take 'product' out of list_display. So how can I display 'product' in list_display without giving it errors?

edit: Maybe a better question would be how do you display a ManyToManyField in list_display?

Answer

karthikr picture karthikr · Aug 7, 2013

You may not be able to do it directly. From the documentation of list_display

ManyToManyField fields aren’t supported, because that would entail executing a separate SQL statement for each row in the table. If you want to do this nonetheless, give your model a custom method, and add that method’s name to list_display. (See below for more on custom methods in list_display.)

You can do something like this:

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

OR define a model method, and use that

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

and in the admin list_display

list_display = ('get_products', 'vendor')