pauzca
pauzca

Reputation: 164

Django template tags all() and count() don't work what should I do

I am very new to Django so bare with me.

I have a model called Posts that looks like this:

class Post(models.Model):
    author = models.ForeignKey(User,on_delete=CASCADE, related_name='posts')
    content = models.TextField(max_length=127)
    date = models.DateField(auto_now=True)

    objects = PostManager()

    def __str__(self):
        return f"{self.author} : {self.content} -- ({self.date})"

And a model of Likes that looks like this:

class Like(models.Model):
    post = models.ForeignKey(Post, on_delete=CASCADE, related_name='likes')
    user = models.ForeignKey(User, on_delete= CASCADE, related_name='liked')
    
    class Meta:
        unique_together = ('post', 'user')

From the Like model I have a foreign key that points to my Post model. If I want to access the amount of likes a post has I write this in the shell:

post1 = Post.objects.get(id =1)
post1.likes.all().count()

This is my view for rendering posts

@require_GET
def getposts(request):
    posts = Post.objects.order_by('-date').all()
    p = Paginator(posts, 10)
    someposts = p.page(int(request.GET.get('page', 1)))

    rendered_posts = render_to_string('network/posts.html', context={'page':someposts})
    
    return JsonResponse({'posts': rendered_posts})

This is how post.html looks like

{% for post in page.object_list%}
<div class="post">
    <ul class="post__head">
        <li class="author">{{post.author.username}}</li>
        <li class="date">{{post.date}}</li>
    </ul>
    <p class="content">{{post.content}}</p>
    <div class="post__actions">
        <span class="like__button"></span>
        <p class="like__amount">{{post.likes.all().count()}}</p>   
    </div>
</div>
{% endfor %}

Now this line is what's giving me trouble:

<p class="like__amount">{{post.likes.all().count()}}</p>   

I get an error that I can't use all() and count() in my template

I was wondering if there's a way of making a function that returns the like count when i call it. Something in models.py like:

def getLikes():
   return post.likes.all().count()

and if I call post1.getLikes() it returns the number of likes in post1?

Thank you and Please help!

Upvotes: 1

Views: 1093

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476557

Django's template engine is deliberately restricted to prevent people from writing business logic in a template: a template should contain rendering logic. Usually it is better to do this through the views.

We can make a queryset that contains the like_count with:

@require_GET
def getposts(request):
    #         add annotation ↓
    posts = Post.objects.annotate(
        like_count=Count('likes')
    ).order_by('-date')
    
    p = Paginator(posts, 10)
    someposts = p.page(int(request.GET.get('page', 1)))

    rendered_posts = render_to_string('network/posts.html', context={'page':someposts})
    return JsonResponse({'posts': rendered_posts})

The advantage of annotating, is that this means that the number of likes are determined in the same query where we load the Post objects, so it will slightly slow down that query, but it will only make one query, whereas using post.likes.count() will make an extra query per Post item.

Then we can render this as:

<p class="like__amount">{{ post.like_count }}</p>

Upvotes: 1

allexiusw
allexiusw

Reputation: 1744

You are about to achieve the goal: When you work in templates you don't have to use () just call methods from templates like this:

{{post.likes.all.count}}

But if you want to abstract the logic to the model that 'is better' you can do the following:

class Post(models.Model):
    author = models.ForeignKey(User,on_delete=CASCADE, related_name='posts')
    content = models.TextField(max_length=127)
    date = models.DateField(auto_now=True)

    objects = PostManager()

    def __str__(self):
        return f"{self.author} : {self.content} -- ({self.date})"
    
    def getLikes(self):
        return self.likes.all().count()

Then you can call it in your template:

{{post.getLikes}}

That's it.

Upvotes: 1

Related Questions