I'm building a news app that allows members to post comments on articles. I want to display the articles in my template and also display the number of comments that have been made on each article. I tried using the count method but, it retrieved the total number of comments in my comments table instead of the number of comments that a particular article has.
#models.py
class Article(models.Model):
#auto-generate indices for our options
ENTRY_STATUS = enumerate(('no', 'yes'))
#this will be a foreign key once account app is built
author = models.CharField(default=1, max_length=1)
category = models.ForeignKey(Category)
title = models.CharField(max_length=50)
entry = models.TextField()
dateposted = models.DateTimeField(default=timezone.now, auto_now_add=True)
draft = models.IntegerField(choices=ENTRY_STATUS, default=0)
lastupdated = models.DateTimeField(default=timezone.now, auto_now=True)
#prevents the generic labeling of our class as 'Classname object'
def __unicode__(self):
return self.title
class Comment(models.Model):
#this will be a foreign key once account app is built
author = models.CharField(default=1, max_length=1)
article = models.ForeignKey(Article)
dateposted = models.DateTimeField(auto_now_add=True)
comment = models.TextField()
def __unicode__(self):
#returns the dateposted as a unicode string
return unicode(self.dateposted)
#templates/list_articles.html
{% for comment in comments %}
{% if comment.article_id == article.id %}
{% if comments.count < 2 %}
#this is returning all comments in comment table
<b>{{ comments.count }} comment</b>
{% else %}
<b>{{ comments.count }} comments</b>
{% endif %}
{% endif %}
{% endfor %}
All the examples I've seen so far manually provide a value to filter by(e.g. Comment.objects.filter(article_id=x).count()
) In my case I only have access via the template.
#views.py
class ArticlesListView(ListView):
context_object_name = 'articles'
# only display published pieces (limit 5)
queryset = Article.objects.select_related().order_by('-dateposted').filter(draft=0)[:5]
template_name = 'news/list_articles.html'
# overide this to pass additional context to templates
def get_context_data(self, **kwargs):
context = super(ArticlesListView, self).get_context_data(**kwargs)
#get all comments
context['comments'] = Comment.objects.order_by('-dateposted')
#get all article photos
context['photos'] = Photo.objects.all()
#context['total_comments'] = Comment.objects.filter(article_id=Article)
return context
My intended result is to have a listing of all articles and a roll-up of comments made on that article below each article(e.g. Article 1: 4 comments, Article 5: 1 comment, etc...) Right now I'm getting: Article 1: 4 comments, Article 5: 4 comments(even though Article 5 only has 1 comment)
Any help is appreciated. I've spent 5 hours reading through the documentation but every example manually provides a value to filter by.
I'm not sure why you find this unexpected. comments
is all the comments, so of course comments.count
is a count of all the comments. How could it be otherwise? You don't filter them anywhere.
This is however a really really horrible way to do things. There is absolutely no reason to pass all comments to the template and then iterate through them to check if they're the right article. You have a foreign key from Comment to Article, so you should use the reverse relationship to get the relevant commments.
Leave out the Comment query altogether from your view, and in your template just do this (replacing that whole block of nested fors and ifs):
{{ article.comment_set.count }}
This however does one count query per article. A better solution is to use annotations, so you can do it all in one single query. Change your queryset to add the annotated count of related comments:
from django.db.models import Count
class ArticlesListView(ListView):
queryset = Article.objects.select_related().annotate(comment_count=Count('comments')).order_by('-dateposted').filter(draft=0)
and now you can just do
{{ article.comment_count }}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With