Abhijit Raj
Abhijit Raj

Reputation: 13

Django Rest Framework: Count Number of Objects with multiple ManyToManyField values

In the example below, I have two models with a ManyToMany relation and I'm trying to count the number of posts related to the tags.


There are 3 tags: Sports, Films, Health
There are 3 posts: 1 for Sports, 1 for Films and 1 post with two tags (Sports and Health)
I can get the count of posts for every tag as below:

[
    {
        "name": "Sports",
        "posts": 2
    },
    {
        "name": "Films",
        "posts": 1
    },
    {
        "name": "Health",
        "posts": 1
    }

]

My requirement is to count the objects separately for the combination of tags. So, the desirable output is:

[
    {
        "name": "Sports",
        "posts": 1
    },
    {
        "name": "Films",
        "posts": 1
    },
    {
        "name": "Sports & Health",
        "posts": 1
    }

]

This is where I'm stuck. How do I combine the two tags and then count the number of post objects that have both these tags?

models.py

class Tag(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    tags = models.ManyToManyField(Tag)

    def __str__(self):
        return self.title

serializers.py

class TagSerializer(serializers.ModelSerializer):

    posts = serializers.SerializerMethodField()

    class Meta:
        model = Tag
        fields = ('name', 'posts')

    def get_posts(self, obj):
        posts = obj.post_set.all().count()
        return posts


class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = '__all__'

Upvotes: 1

Views: 5247

Answers (4)

Smokey Akihabara
Smokey Akihabara

Reputation: 1

def get_counter(self, obj):
    counter=Post.objects.filter(tags=obj).count()
    return counter

Upvotes: 0

ameen alakhras
ameen alakhras

Reputation: 162

I would recommend adding a related field in the Post model to the ManyToMany relation ease of calling the many to many relation from the tags side.

tags = models.ManyToManyField(Tag, related_name="posts_objects")

Instead of adding SerializerMethodField you can simply posts_objects.count in serializer:

class TagSerializer(serializers.ModelSerializer):
    posts_objects = serializers.HiddenField()
    posts = serializers.IntegerField(source="posts_objects.count", read_only=True)


    class Meta:
        model = Tag
        fields = ('name', 'posts_objects', 'posts')

Upvotes: 2

Abhijit Raj
Abhijit Raj

Reputation: 13

Currently, I managed to have a working solution by counting the number of objects for every tag in the API result on the frontend. It works for my case, as the data returned is not huge.

let tagDict = {}
let data = response.data
  for (let i = 0; i < data.length; i++) {
    let key = data[i].name
      if ( key in tagDict ) { 
          tagDict[key] += 1
        } else {
          tagDict[key] = 1 
        }   
    }   

Marking this as accepted until a better solution comes along.

Upvotes: 0

kamilyrb
kamilyrb

Reputation: 2627

use this in your serializers.py get_posts

Post.objects.filter(tags__in=[obj]).count()

instead of

obj.post_set.all().count()

Upvotes: 1

Related Questions