Reputation: 164
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
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
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