Как сделать подсчет посещений отдельным для каждого элемента

Здравствуйте, я делаю приложение на django, я хотел бы добавить функцию счетчика посещений, но отдельно для каждого элемента. Я думаю, это будет хорошая функция.

def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['comments'] = Comment.objects.filter(item=self.object)
        context['form'] = CommentCreationForm()
        num_visits = self.request.session.get('num_visits', 0)
        self.request.session['num_visits'] = num_visits + 1
        context['visits'] = num_visits
        return context

В настоящее время то, что вы реализуете, - это счетчик на сессию . Фактически это означает, что если пользователь начинает сессию на вашей странице, то сначала он увидит ноль, затем один, и так далее. Но при этом будет учитываться только то, сколько раз этот пользователь посетит страницу за сессию. Посещения других пользователей не будут иметь никакого значения.

Если вы хотите отслеживать общее количество посещений каждого пользователя, вам нужно, чтобы данные о посещениях сохранялись. Вы можете сделать это с помощью дополнительной модели, которая, например, каждый раз создает новую запись, когда (зарегистрированный) пользователь посещает страницу, или мы можем работать с простым счетчиком. Если мы хотим предотвратить подсчет одного и того же пользователя несколько раз, если он посещает один и тот же объект несколько раз, то логичнее работать со счетчиком ManyToManyField для пользователя.

Вариант 1: простой IntegerField

Простая реализация, которая просто считает количество посещений, и, таким образом, считает одного и того же пользователя дважды, если этот пользователь посещает объект дважды, может быть реализована с дополнительным IntegerField для подсчета количества посещений, это выглядит так. Мы можем написать абстрактную модель для этого:

class WithVisitCounter(models.Model):
    visits = models.IntegerField(editable=False, default=0)

    class Meta:
        abstract = True

и затем пусть модель наследуется от этого:

class BlogPost(WithVisitCounter, models.Model):
    # ⋮

тогда мы можем сделать миксин WithVisitCounterMixin:

from django.views.generic.detail import SingleObjectMixin

class WithVisitCounterMixin(SingleObjectMixin):

    def get_object(self, *args, **kwargs):
        obj = super().get_object(*args, **kwargs)
        old_visit = obj.visits
        obj.visits = F('visits') + 1
        obj.save(updated_fields=['visits'])
        obj.visits = old_visit + 1
        return obj

    def get_context_data(self, *args, **kwargs):
        cd = super().get_context_data(*args, **kwargs)
        cd['visits'] = self.object.visits
        return cd

Тогда мы можем использовать этот Mixin во всех представлениях, которые имеют SingleObjectMixin, как DetailView и UpdateView:

class BlogPostDetailView(WithVisitCounterMixin, DetailView):
    # ⋮

Это передаст количество посетителей как visits в контекстные данные, так что вы можете отобразить это с помощью {{ visits }}, или с помощью {{ object.visits }}, если объект передается в шаблон.

Вариант 2: A ManyToManyField к модели пользователя

Первый вариант не учитывает пользователей, которые посещают один и тот же объект несколько раз. Это означает, что один и тот же пользователь может посетить страницу двадцать раз, и это будет рассматриваться как двадцать независимых посещений.

В этом случае мы можем определить абстрактную модель, которая будет добавлять ссылки на пользователей, с:

from django.conf import settings

class WithVisitCounter(models.Model):
    visitors = models.ManyToManyField(
        to=settings.AUTH_USER_MODEL,
        related_name='%(model_name)s_visits'
    )

    class Meta:
        abstract = True

class BlogPost(WithVisitCounter, models.Model):
    # ⋮

Затем мы можем определить WithVisitCounterMixin так же, как и в первом варианте. В этом случае мы добавим ссылку от объекта к вошедшему пользователю:

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.detail import SingleObjectMixin

class WithVisitCounterMixin(SingleObjectMixin):

    def get_object(self, *args, **kwargs):
        obj = super().get_object(*args, **kwargs)
        obj.visitors.add(self.request.user)
        return obj

    def get_context_data(self, *args, **kwargs):
        cd = super().get_context_data(*args, **kwargs)
        cd['visits'] = self.object.visitors.count()
        return cd

для этого единственного объекта, мы можем затем получить посетителей, подсчитав количество записей для .visitors из self.object.

Таким образом, мы можем использовать этот миксин в DetailView или UpdateView, а также:

class BlogPostDetailView(WithVisitCounterMixin, DetailView):
    # ⋮

Затем мы можем снова использовать {{ visits }} для количества посетителей для этого элемента.

Вернуться на верх