We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
In my blog, I wanted an easy way to count the number of views for a specific blog post without having to rely on Google Analytics. With the recent browser updates, libraries like Google Analytics are often disabled and shouldn't be relied on.
Therefor, I wanted a solution which does the count server-side and store them in the database.
The de-facto solution for doing this with Django is a module called django-hitcount. Getting it installed is the same as with every Django extension. We first use the pip
utility to get the module installed:
$ pip install django-hitcount
Then, we need to add it to the list of installed applications in the settings.py
file of our app:
mysite/settings.py
INSTALLED_APPS = [
...
'hitcount',
...
]
I then updated the model class to allow sorting on the hitcount:
blog/models.py
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation
from hitcount.models import HitCountMixin, HitCount
class Post(models.Model, HitCountMixin):
title = models.CharField(max_length=255, unique=True)
slug = models.SlugField(max_length=255, unique=True)
published_on = models.DateTimeField(blank=True, default=None, null=True)
content = models.TextField(blank=True)
hit_count_generic = GenericRelation(
HitCount, object_id_field='object_pk',
related_query_name='hit_count_generic_relation'
)
def current_hit_count(self):
return self.hit_count.hits
The GenericRelation
allows you so query the posts sorting them by their hitcount:
Post.objects.order_by("hit_count_generic__hits")
The current_hit_count
makes it easy to display the number of views in the template:
blog/templates/blog/post_detail.html
{% if post.current_hit_count > 1 %}
| {{ post.current_hit_count }} views
{% endif %}
Since I'm using class-based views for my blog detail pages, I can update the view to:
blog/views.py
from .models import Post
from hitcount.views import HitCountDetailView
class PostDetail(HitCountDetailView):
model = Post
template_name = 'blog/post_detail.html'
count_hit = True
def get_context_data(self, **kwargs):
context = super(PostDetail, self).get_context_data(**kwargs)
context.update({
'popular_posts': Post.objects.order_by('-hit_count_generic__hits')[:3],
})
return context
Instead of extending from DetailView
, I'm now extending from HitCountDetailView
instead. For the curious, HitCountDetailView
is a combination of DetailView
along with HitCountMixin
which adds the hit counting capabilities.
To actually count the hits for this view, you need to add count_hit=True
. This will count every time the view is rendered. I also added the get_context_data
method so that I can add the list of popular posts to the blogpost detail page. To get these, I'm getting the posts ordered by their hit count in descending order, limiting the result to 3.
To show the related posts in your template, you can use:
blog/templates/blog/post_detail.html
<h3>Popular Posts</h3>
{% for p in popular_posts %}
<p>{{p.title}}</p>
{% endfor %}
Last but not least, I wanted to add the view count to the list view of the blog posts in the admin view:
blog/admin.py
from .models import Post
from django.contrib import admin
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'formatted_hit_count',)
def formatted_hit_count(self, obj):
return obj.current_hit_count if obj.current_hit_count > 0 else '-'
formatted_hit_count.admin_order_field = 'hit_count'
formatted_hit_count.short_description = 'Hits'
admin.site.register(Post, PostAdmin)
The complete documentation for Django hitcount can be found here.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.