Omid Shojaee
Omid Shojaee

Reputation: 347

Hit counter for Django blog post

In my search to find a way to add hit counter to my blog posts without any third party library I found this answer on StackOverflow.

However as I'm not a Django expert I can't figure out how to use that Mixin with my view.

Here's how my model is defined:

class Post(models.Model):
    STATUS_CHOICES = (('draft', 'Draft'), ('published', 'Published'))
    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250, unique_for_date='publish')
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
    body = models.TextField()
    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')

And my views:

def post_list(request):
    posts = Post.published.all()
    return render(request, 'blog/post/list.html', {'posts': posts})


class PostDetailView(DetailView):
    model = Post
    context_object_name = 'post'

And here's the mixin provided in that answer:

class BlogPostCounterMixin(object):
    def get_context_data(self, **kwargs):
        context = super(BlogPostCounterMixin, self).get_context_data(**kwargs)
        blog_post_slug = self.kwargs['slug']
        if not blog_post_slug in self.request.session:
            bp = BlogPost.objects.filter(slug=blog_post_slug).update(counter=+1)
            # Insert the slug into the session as the user has seen it
            self.request.session[blog_post_slug] = blog_post_slug
    return context

I would ask the user provided that answer, but he's inactive for more than 2 years.

Appreciate your help.

Upvotes: 1

Views: 182

Answers (1)

Jameel Hamdan
Jameel Hamdan

Reputation: 321

you can create a IntegerField called views in your Model

then in your post_detail view you could do this just before return statement

post.views += 1
post.save(update_fields=['views'])

Obviously this solution has the drawback of some views that happen at exactly the same moment being missed.

Edit:

before using a mixin you must first use a class based view instead of a function based view then you can use this mixin and in your case you would want to use a DetailView

https://docs.djangoproject.com/en/3.2/ref/class-based-views/generic-display/#detailview

class BlogPostCounterMixin:
    def get_object(self, *args, **kwargs):
        # get_object will be available if you use a DetailView
        obj = super().get_object(*args, **kwargs):
        post_unique_key = 'post_%s' % obj.pk # or whatever unique key is
        if not post_unique_key in self.request.session:
            obj.views += 1
            obj.save(update_fields=['views'])
            self.request.session[post_unique_key] = post_unique_key

        return obj

and can be used like this

class PostDetailView(BlogPostCounterMixin, DetailView):
    model = Post
    context_object_name = 'post'

Upvotes: 1

Related Questions