Evgenii Legotckoi
Evgenii LegotckoiMarch 19, 2023, 10:56 a.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:
                self.object.viewers.add(viewer)

        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 }}

get_client_ip

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')

Conclusion

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!

Comments

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

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

  • Result:50points,
  • Rating points-4
m

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

  • Result:80points,
  • Rating points4
m

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

  • Result:20points,
  • Rating points-10
Last comments
ИМ
Игорь МаксимовNov. 22, 2024, 5:51 p.m.
Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiOct. 31, 2024, 7:37 p.m.
Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEOct. 19, 2024, 2:19 p.m.
Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовOct. 5, 2024, 1:51 p.m.
Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5July 5, 2024, 5:02 p.m.
QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Now discuss on the forum
Evgenii Legotckoi
Evgenii LegotckoiJune 24, 2024, 9:11 p.m.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Nov. 15, 2024, 12:04 p.m.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectJune 4, 2022, 9:49 a.m.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimOct. 25, 2024, 3:10 p.m.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Follow us in social networks