1

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__'
4
  • Your codes looks correct. Are you sure you have 1 post record for sport tag? Commented Feb 20, 2020 at 6:48
  • Yes, I have made 3 posts. One is for Sports only and one other has two tags: Sports and Health. So it is counted as 2 for sports. What i want is to count it separately with combined category (Sports and Health) Commented Feb 20, 2020 at 7:00
  • it is possible to add multiple tags to a post. Which might be happened here Commented Feb 20, 2020 at 7:52
  • @ruddra: Yes, the default behavior is okay. I want the modification as described in the question which is where I'm stuck. Commented Feb 20, 2020 at 10:02

4 Answers 4

2

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')
Sign up to request clarification or add additional context in comments.

Comments

1

use this in your serializers.py get_posts

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

instead of

obj.post_set.all().count()

2 Comments

This does not create a combined category of tags (Sports & Health). I am inclined to think that this task would be better suited on the frontend. I could return a list of posts with their tags from the backend and then count the posts based on the tag combinations in the frontend. Do you agree?
Yes you can do it, it is not bad way, I think.
0

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.

Comments

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

1 Comment

This answer was reviewed in the Low Quality Queue. Here are some guidelines for How do I write a good answer?. Code only answers are not considered good answers, and are likely to be downvoted and/or deleted because they are less useful to a community of learners. It's only obvious to you. Explain what it does, and how it's different / better than existing answers. From Review

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.