Home > Software design >  django many-to-many-field in list display for item like count
django many-to-many-field in list display for item like count

Time:09-30

I want to display total likes against each item in Django admin list_display. The many to many field is linked with the user model. Below is my code

Model:

class Item(models.Model):
    title = models.CharField(max_length=100)
    description= RichTextField(blank=True, null=True)
    main_image= models.ImageField(null=False, blank=False,upload_to='images/')
    upload = models.FileField(upload_to ='uploads/')
    date = models.DateTimeField(auto_now_add=True)
    item_category = models.ForeignKey(Categories, default='Coding', on_delete=SET_DEFAULT)
    item_tool = models.ForeignKey(Tools, default='XD', on_delete=SET_DEFAULT)
    slug = models.SlugField(unique=True, blank=True, null=True) # new
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    likes = models.ManyToManyField(User, related_name='post_likes')

    def total_likes(self):
        return self.likes.count()

Admin.py

from django.contrib import admin
from .models import Categories, Item, Tools

class ItemAdmin(admin.ModelAdmin):
    list_display = ('title','author','item_category','date')
    list_filter = ('item_category',)


admin.site.register(Categories)
admin.site.register(Item, ItemAdmin)
admin.site.register(Tools)

CodePudding user response:

You can add a custom display to your list_display to return the number of likes like this:

class ItemAdmin(admin.ModelAdmin):
    list_display = ('title','author','item_category','date','likes_count')
    list_filter = ('item_category',)

    def likes_count(self, obj):
        return len(obj.likes.all())
    likes_count.short_description = 'Likes'

    def get_queryset(self, *args, **kwargs):
        return super().get_queryset(*args, **kwargs).prefetch_related('likes')

Note the additional change for get_queryset to help reduce db hits by using prefetch_related. Without it, each row of Item instances will hit the database to get the number of likes.

UPDATE:

Another way to do this is to annotate each item with the total number of likes like this:

class ItemAdmin(admin.ModelAdmin):
    list_display = ('title','author','item_category','date','likes_count')
    list_filter = ('item_category',)

    def likes_count(self, obj):
        return obj.likes_count
    likes_count.short_description = 'Likes'
    likes_count.admin_order_field = 'likes_count'

    def get_queryset(self, *args, **kwargs):
        return super().get_queryset(*args, **kwargs).annotate(likes_count=Count('likes'))

This will support ordering by the number of likes by using admin_order_field.

  • Related