1

I want to do the following: every time a user enters an article, it increments the visit by +1, but only the first time the user logs in, so I have to save the fact that the user has already entered the page in some place. But I'm not using authentication or anything like that.

I know I can use javascript to store in LocalStorage, but I still do not know how to work with APIS in the back end.

What's the easiest way to do this on the backend?

Currently the function that increments is as below. NOTE: I create a new object instead of using something like "instance.visits + = 1" because I need to save the date of each visit to filter the posts with more visits in a certain period of time, and that was the only way I got it.

class ArticlePage(Page):
    # ....

    def serve(self, request, *args, **kwargs):
        request.is_preview = getattr(request, 'is_preview', False)

        self.views.create(date=datetime.datetime.now())
        self.save()
        print(self.views.all().count())

        return TemplateResponse(
            request,
            self.get_template(request, *args, **kwargs),
            self.get_context(request, *args, **kwargs)
        )

class ArticlePageViews(models.Model):
    article = models.ForeignKey(
        ArticlePage,
        on_delete=models.CASCADE,
        related_name='views'
    )
    date = models.DateTimeField()

    def __str__(self):
        return f'{self.date}'
marcos souza
  • 701
  • 1
  • 10
  • 20

2 Answers2

2

Here is the possibility using coockies

class ArticlePage(Page):
    # ....

    def serve(self, request, *args, **kwargs):
        request.is_preview = getattr(request, 'is_preview', False)
        if not 'cookie_name' in request.COOKIES:
          self.views.create(date=datetime.datetime.now())
          self.save()
        print(self.views.all().count())

        response=TemplateResponse(
            request,
            self.get_template(request, *args, **kwargs),
            self.get_context(request, *args, **kwargs)
        )
        response.set_cookie('cookie_name', 'cookie_value')
        return response

refer this to set expire time for coockie

Pavan Kumar T S
  • 1,539
  • 2
  • 17
  • 26
2

Since you only want one view per user, you can't store the views in the user session, because the session changes when a user logs in. I would suggest you set a UUID cookie the first time a user visits your website and set a long expiration date, like 10 years. This can be done with a middleware:

# middleware.py
import uuid


class UUIDMiddleware:
    """
    Middleware to set a UUID cookie when a user visits
    for the first time.
    """
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if not request.COOKIES.get('user_uuid'):
            user_uuid = str(uuid.uuid4())
            # set cookie in request so it can be used on the first visit
            request.COOKIES['user_uuid'] = user_uuid

            response = self.get_response(request)

            max_age = 60 * 60 * 24 * 365 * 10  # 10 years
            response.set_cookie('user_uuid',  user_uuid, max_age=max_age)
            return response

        return self.get_response(request)

Remember to add your middleware to the settings. Now in your ArticlePageViews model you can set a unique_together constraint:

class ArticlePageViews(models.Model):
    article = models.ForeignKey(
        ArticlePage,
        on_delete=models.CASCADE,
        related_name='views'
    )
    user_uuid = models.UUIDField()
    date = models.DateTimeField(auto_now_add=True)  # set datetime automatically

    class Meta:
        unique_together = ('article', 'user_uuid')

    def __str__(self):
        return f'{self.date}'

and in your view (just an example):

def article_view(request, article_id):
     article = get_object_or_404(ArticlePage, pk=article_id)
     user_uuid = request.COOKIES.get('uuid_cookie')
     if user_uuid is not None:  # user_uuid may not be set yet
         try:
             ArticlePageViews.objects.create(article=artricle, user_uuid=user_uuid)
         except IntegrityError:
             #  unique_together validation failed, so the user already viewed this article
             pass  # don't do anything
     ...
p14z
  • 1,760
  • 1
  • 11
  • 17
  • You can also store the uuid in a Model to make sure it doesn't get repeated, but the chances are few. – p14z Mar 16 '19 at 01:35