Evgenii Legotckoi
Evgenii LegotckoiMarch 19, 2023, 8:56 p.m.

Django - Tutorial 061. Adding a Unique View Count

At the very beginning of creating articles and questions on the forum, I added a simple counter of views on this content.
This counter was an ordinary field of integer type and each time a page was requested it was incremented by one.
But not so long ago, I replaced this counter with a counter model for registering unique views. I just did it because I think it's prettier.

The counter counts unique visitors either by IP address, if the user is not authorized on the site, or by user account, if this user is authorized on the site.

So let's learn how to do it.

Viewer model

Responsible for counting unique users who viewed the site.

class Viewer(models.Model):
    ipaddress = models.GenericIPAddressField("IP address", blank=True, null=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, blank=True, null=True)

As you can see, there are only two fields in this model, the first is the IP Address, and the second is the foreign key to the user model

Adding a counter to the content model

And now let's add a counter to some site content model, for example, to the article model Article

class Article(models.Model):
    viewers = models.ManyToManyField(Viewer)

    # Some another code

Adding code is done as a Many-To-Many relationship. Since the unique visitor in this case will be one, but he can view many other articles. At the same time, the article can be viewed by many other users.

How to increase the content view counter

Most likely, some view class or view function will be responsible for displaying articles on your site. Personally, I prefer to use modern Class Based View in Django.

Therefore, I will write a mixin that can be used with DetailView to register unique views of content on the site.

class CountViewerMixin:

    def get(self, request, *args, **kwargs):
        response = super().get(request, *args, **kwargs)
        if hasattr(self.object, 'viewers'):
            viewer, created = Viewer.objects.get_or_create(
                ipaddress=None if request.user.is_authenticated else get_client_ip(request),
                user=request.user if request.user.is_authenticated else None

            if self.object.viewers.filter(id=viewer.id).count() == 0:

        return response

It can be seen from the code that the mixin overrides the get method, checks that the content object exists in the View and only after that gets a unique visitor, and after checking that the visitor does not yet exist among the visitors who have viewed the content, adds it to the content views.

Thus, the content object itself is not modified, unlike the counter in the form of an integer field in the content model. What can actually complicate the code is if you have content model save method overrides or you have written receiver functions to handle save signals from the model. In the case of the Viewer counter, which is presented in this article, signals such as post_save on the content model simply do not work.

In the Content View, the mixin connection will look like this:

class ArticleView(CountViewerMixin, DetailView):
    # some another code

Usage in template

Inside the template, you can get the number of views as follows

{{ object.viewers.count }}


The mixin also has a function to get an ip address from an HTTP request. I have already described this special function for getting the user's ip address from a request in one of the articles .

And in my latest revision for Django 3, it looks like this.

def get_client_ip(request):
    Get client ip address from HTTP request

    :param request: HTTP request
    :return: IP Address
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    return x_forwarded_for.split(',')[-1].strip() if x_forwarded_for else request.META.get('REMOTE_ADDR')


Such a counter will make it easier to maintain the code if you have reactions to saving content, and will also reduce the impact of cheats on content counters.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!


Only authorized users can post comments.
Please, Log in or Sign up

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:70points,
  • Rating points1

Qt - Test 001. Signals and slots

  • Result:84points,
  • Rating points4
Last comments
kmssrFeb. 9, 2024, 5:43 a.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVADec. 25, 2023, 9:30 p.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
JonnyJoDec. 25, 2023, 7:38 p.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
GvozdikDec. 19, 2023, 8:01 a.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
NSProjectJune 23, 2024, 11:48 p.m.
добавить qlineseries в функции А куда собстаенно делся Евгений раз на сайте такой бордак творится?
BlinCTMay 5, 2024, 3:46 p.m.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiMay 3, 2024, 12:07 a.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderCheApril 30, 2024, 2:22 p.m.
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…
GarApril 22, 2024, 3:46 p.m.
Clipboard Как скопировать окно целиком в clipb?

Follow us in social networks