Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: get last user visit date

Tags:

django

In Django, we can get the time user last logged in by using Auth.User.last_login. That is only updated when the user logs in using his username/password. Suppose the user is already logged in and the authentication information is saved in a cookie, therefore is able to access the site without logging in. How can we get the date the user previously visited the site? This would be useful for queries such as getting the number of new records since the last visit.

like image 913
user2233706 Avatar asked Aug 25 '13 22:08

user2233706


3 Answers

Example model:

class User(models.Model):
    last_visit = models.DateTimeField(...)
    ...

Example middleware which will be executed for all logged-in users:

from django.utils.timezone import now

class SetLastVisitMiddleware(object):
    def process_response(self, request, response):
        if request.user.is_authenticated():
            # Update last visit time after request finished processing.
            User.objects.filter(pk=request.user.pk).update(last_visit=now())
        return response

Add the new middleware to Your settings.py:

MIDDLEWARE_CLASSES = (
    ...
    'path.to.your.SetLastVisitMiddleware',
    ...
)

Warning: not tested, but doesn't require external packages to be installed and it's only 5 lines of code.

See more in the docs about Middleware and custom user models (since Django 1.5)

like image 124
HankMoody Avatar answered Oct 06 '22 17:10

HankMoody


Here's a middleware that will keep track of user last activity and count separated by intervals of time. Using the interval creates discrete "sessions" which can be tracked/counted along with the benefit of minimizing writes to the database.

Every time an auth user performs a request, will hit the cache to find their last activity, and then update the cache with a new timestamp. If the activity has had a gap of at least "interval" time, then it will update the database timestamp.

from datetime import timedelta as td
from django.utils import timezone
from django.conf import settings
from django.db.models.expressions import F    
from <user profile path> import UserProfile  

class LastUserActivityMiddleware(object):
    KEY = "last-activity"

    def process_request(self, request):
        if request.user.is_authenticated():
            last_activity = request.session.get(self.KEY)

            # If key is old enough, update database.
            too_old_time = timezone.now() - td(seconds=settings.LAST_ACTIVITY_INTERVAL_SECS)
            if not last_activity or last_activity < too_old_time:
                UserProfile.objects.filter(user=request.user.pk).update(
                        last_login=timezone.now(),
                        login_count=F('login_count') + 1)

            request.session[self.KEY] = timezone.now()

        return None

Comments:

  1. How you define settings.LAST_ACTIVITY_INTERVAL_SECS determine what constitutes the interval of non-activity considered to be a new login.
  2. This updates a "UserProfile" object which I have 1:1 with my User objects, but you can update any object you please.
  3. Make sure to include this in settings.MIDDLEWARE_CLASSES.
  4. Note this middleware uses process_request not process_response otherwise depending on middleware order, APPEND_SLASH may cause request.user to be unavailable as discussed: Django: WSGIRequest' object has no attribute 'user' on some pages?
like image 25
John Lehmann Avatar answered Oct 06 '22 17:10

John Lehmann


Taking into account @John Lehmann solution and @Andrew Swihart suggestions, I came up with this code for newer versions of Django (> 2.0):

from datetime import timedelta as td
from django.utils import timezone
from django.conf import settings
from django.db.models.expressions import F
from dateutil.parser import parse

from .models import Account


class AccountLoginMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.user.is_authenticated:
            last_activity = request.session.get('last-activity')

            too_old_time = timezone.now() - td(seconds=settings.LOGIN_INTERVAL)
            if not last_activity or parse(last_activity) < too_old_time:
                Account.objects.filter(username=request.user.username).update(
                    login_last=timezone.now(),
                    login_count=F('login_count') + 1)

            request.session['last-activity'] = timezone.now().isoformat()

        response = self.get_response(request)

        return response
like image 4
j4n7 Avatar answered Oct 06 '22 16:10

j4n7