Search code examples
djangodjango-models

Refer to another model and count its ManytomanyField


I have two models:

class Post(models.Model):
    content = models.TextField(blank=False)
    author = models.ForeignKey("User", on_delete=models.CASCADE, related_name="author")
    timestamp = models.DateTimeField(auto_now_add=True)
    
    def serialize(self):
        likes_count = self.liked_by.all().count()
        
        return {
            "id": self.id,
            "content": self.content,
            "author": self.author.username,
            "author_id": self.author.id,
            "timestamp": self.timestamp.strftime("%b %d %Y, %I:%M %p"),
            "likes": likes_count
        }

and

class Liking_System(models.Model):
    users_who_like = models.ManyToManyField("User", related_name="users_liked")
    post = models.ForeignKey("Post", on_delete=models.CASCADE, related_name="liked_by")

    def __str__(self):
        return f"Post {self.post.id} is liked by {', '.join([user.username for user in self.users_who_like.all()])}"

I want in my Post model, it linked to the Liking_System and count how many users that has liked the post and return it. How can I do that? How do I modify the "likes_count = self.liked_by.all().count()". For example my post#1 is liked by two users A and B, but it returned only 1?


Solution

  • Easiest Way:

    Remove Liking_System-model entirely. It does not really make sense to have multiple Liking_Systems, at least from the perspective of your provided code snippet.

    class Post(models.Model):
        content = models.TextField(blank=False)
        author = models.ForeignKey("User", on_delete=models.CASCADE, related_name="posts")
        timestamp = models.DateTimeField(auto_now_add=True)
        likers = models.ManyToManyField("User", related_name="liked_posts", blank=True)  # Add this line
    
        def serialize(self):
            likes_count = self.likers.count()  # Modify this line
    
            return {
                "id": self.id,
                "content": self.content,
                "author": self.author.username,
                "author_id": self.author.id,
                "timestamp": self.timestamp.strftime("%b %d %Y, %I:%M %p"),
                "likes": likes_count
            }
    

    Exact answer to your question

    If you really need separate liking systems it looks a bit different. Let's imagine you have a blue and a yellow liking systems. Depending on the color of the like button different liking systems are used. And on your Post model you want to display / count the number of total likes of all the liking systems. The code snippet you're providing likes_count = self.liked_by.all().count() only counts the number of different liking systems connected to your post.

    # explanatory noob version
    likes_count = 0
    for i in self.liked_by.all():
        likers_count += i.users_who_like.count()
    
    # pro / djangonic version
    likes_count = self.liked_by.aggregate(likes=Count('users_who_like'))['likes'] or 0
    

    This loops through all your liking systems, the yellow, the blue, the red one and adds the count of users of each liking system together.

    This is super uncommon. I think you should be good to go with the "Easiest Way".