many-to-many in list display django
Solution 1
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')
Solution 2
This way you can do it, kindly checkout the following 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)
And in your admin.py module call method as follows:
class categories(admin.ModelAdmin):
list_display = ('title', 'get_parents', 'when')
Solution 3
If you want to save extra queries, you can use prefetch_related in the get_queryset
method like below:
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()])
According to the Docs, In this way, there would be just one extra query needed to fetch related Product
items of all PurchaseOrder
instances instead of needing one query per each PurchaseOrder
instance.
Related videos on Youtube
Mdjon26
Updated on July 08, 2022Comments
-
Mdjon26 almost 2 years
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')
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 oflist_display
. So how can I display'product'
inlist_display
without giving it errors?edit: Maybe a better question would be how do you display a
ManyToManyField
inlist_display
? -
Mdjon26 over 10 yearsThis looks like a really good solution. Thank you. Although, I'm now getting an error saying "null value in column "product_id" violates not-null constraint " Any idea what this means?
-
karthikr over 10 yearsThat could be a data issue. Check the values in the database
-
Cloud Artisans almost 7 yearsSince this brings down the database to its knees, how would you do it with select_related() or prefetch_related() to improve performance?
-
goetz almost 7 yearsJust in case the optimisation question is still interesting, I just had the same problem and found out that you could simply implement an optimised
get_queryset()
method for yourModelAdmin
, see stackoverflow.com/questions/12354099/… -
Sebastián Vansteenkiste over 4 yearsThat's an interesting link @goetz but I'm not sure we can all follow on how that relates to improving performance
-
Sebastián Vansteenkiste over 4 yearsActually, What about setting the
get_products
as a property and then using@cached_property
? -
goetz over 4 years@SebastiánVansteenkiste Good idea, maybe
cached_property
would help. But I think it would probably not. When you use an optimisedget_queryset
, you could for example annotate/pre-process the data there, like doing the concatenation of products in SQL instead of in Django, and store your custom data in your queryset. Then you would only need to execute that logic once in SQL and not for every row when the property is accessed. -
Sebastián Vansteenkiste over 4 yearsGood point. I should look into doing my own optimized
get_queryset
, I just haven't got around to reading the full docs (nor found a simple enough example of what I should be doing)