Can not show the category name with number of articles django

I am trying to show number of articles in each category in my django project. But it shows category id instead of category_name. I want to display category_name and the corresponding number of articles.

blog/views.py

def searchView(request):
    statistics = Post.objects.values('cid').annotate(num_articles = Count('cid')).order_by()
    return render(request, 'blog/search.html', {'statistics': statistics})

blog/search.html -> here stat.cid shows the category id but I want to show category_name here.

{% extends 'users/base.html' %}
{% block content %}
<div class="container">
    <br>
    <div class="row text-center">
        <div class="col-md-3"> </div>
        <div class="col-md-6">
            <!-- <% if (statistics.length> 0){ %> -->
                <h4 class="p-2 mb-2 bg-secondary text-white">POPULAR CATEGORIES!!</h4>
                <table id="myTable" class="table table-bordered table-hover table-striped shadow-sm bg-white rounded">
                    <thead>
                        <tr>
                            <th>Category</th>
                            <th>Articles Available</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for stat in statistics %}
                            <tr>
                                <td>
                                    {{ stat.cid }}
                                </td>
                                <td>
                                   {{ stat.num_articles }}
                                </td>
                            </tr>
                        {% endfor %}
                    </tbody>
                </table>
            <!-- <% } %> -->
        </div>
    </div>
</div>
{% endblock content %}

blog/models.py

from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.urls import reverse
from ckeditor.fields import RichTextField

# Create your models here.
class Category(models.Model):
    cid = models.AutoField(primary_key=True) 
    category_name = models.CharField(max_length=100)

    def __str__(self):
        return self.category_name


class Post(models.Model):
    aid = models.AutoField(primary_key=True)
    image = models.ImageField(default='blog-default.png', upload_to='images/')
    title = models.CharField(max_length=200)
    content = RichTextField()
    created = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    cid = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name='specialization')
    approved = models.BooleanField('Approved', default=False)
    like = models.ManyToManyField(get_user_model(), related_name='likes', blank=True)

    def __str__(self):
        return self.title
 

Post.objects.values('cid') would only give you the pk of the Category. To access the category_name of the Category you should also include that in the values():

# Note the double underscore between cid and category_name
`Post.objects.values('cid', 'cid__category_name')`

Now that the category_name is available, access it in the template like this:

{{ stat.cid__category_name }}

While this is specific to your case, a better answer is here:
https://stackoverflow.com/a/27181936/10951070

I would be going at this from the opposite direction, meaning I would be accessing this data from Category rather than from Post which for some reason you call statistics.

First off I'd suggest you to use a ListView and then I'd proceed as follows:

# views

from django.views.generic import ListView

class CategoryListView(ListView):
    model = Category
    template_name = 'blog/search.html'
    context_object_name = "categories"
# template
<table>
    <thead>
    ...
    </thead>
    <tbody>
        {% for category in categories %}
            <tr>
                <td>{{ category.cid }}</td>
                <td>{{ category.post_set.count }}</td>
            </tr>
        {% endfor %}
    </tbody>
</table>

If you add a related_name in your Post model in the cid attribute and call it posts, you can then in the template write category.posts.count which is more readable. This is just a personal preference and definitely not a rule.

Back to Top