Django:获取上次用户访问日期

mv1qrgav  于 2023-01-14  发布在  Go
关注(0)|答案(8)|浏览(120)

在Django中,我们可以使用Auth.User.last_login来获取用户上次登录的时间。只有当用户使用他的用户名/密码登录时,这个信息才会更新。假设用户已经登录,并且验证信息保存在一个cookie中,因此,无需登录即可访问站点。我们如何获取用户以前访问站点的日期?这对于诸如获取自上次访视以来的新记录数量之类的查询非常有用。

lf3rwulv

lf3rwulv1#

示例型号:

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

将为所有登录用户执行的示例中间件:

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

将新中间件添加到您的settings.py:

MIDDLEWARE_CLASSES = (
    ...
    'path.to.your.SetLastVisitMiddleware',
    ...
)
  • 警告:* 未测试,但不需要安装外部包,只有5行代码。

更多关于Middleware和自定义用户模型的文档(Django 1.5之后)

isr3a4wc

isr3a4wc2#

这里有一个中间件,它将跟踪用户的最后一次活动,并按时间间隔进行计数。使用时间间隔创建离散的"会话",可以跟踪/计数这些会话,同时还可以最大限度地减少对数据库的写入。
每次身份验证用户执行请求时,将命中高速缓存以查找其最后一个活动,然后使用新的时间戳更新高速缓存。如果活动之间的间隔至少为"interval"时间,则它将更新数据库时间戳。

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

备注:
1.定义settings.LAST_ACTIVITY_INTERVAL_SECS的方式决定了被视为新登录的非活动时间间隔。
1.这将更新一个"UserProfile"对象,我与我的用户对象是1:1的,但您可以更新任何对象。
1.请确保将其包含在settings.MIDDLEWARE_CLASSES中。
1.请注意,此中间件使用process_request而不是process_response,否则根据中间件订单,APPEND_SLASH可能会导致request.user不可用,如前所述:Django: WSGIRequest' object has no attribute 'user' on some pages?

bihw5rsg

bihw5rsg3#

考虑到@John Lehmann的解决方案和@Andrew Swihart的建议,我为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
xvw2m8pv

xvw2m8pv4#

@John Lehmann的解决方案非常好,但是它需要使用特定的基于缓存的会话设置,以避免每次请求都写入数据库。
在基于高速缓存的会话中有两个选项,backends.cachebackends.cached_db。第二个选项是直写高速缓存,即对会话数据的每次修改都写入数据库和高速缓存。这提供了重新启动后的持久性。

我重新编写了上面的代码,以显式使用该高速缓存函数并避免大量数据库写入。

from django.core.cache import cache
from django.utils import timezone
# other user model import

def last_visit_middleware(get_response):

    def middleware(request):
        """
        Save the time of last user visit
        """
        response = get_response(request)

        if request.session.session_key:
            key = "recently-seen-{}".format(request.session.session_key)
            recently_seen = cache.get(key)

            # is_authenticated hits db as it selects user row
            # so we will hit it only if user is not recently seen
            if not recently_seen and request.user.is_authenticated:
                UserAccount.objects.filter(id=request.user.id) \
                    .update(last_visit=timezone.now())

                visit_time = 60 * 30    # 30 minutes
                cache.set(key, 1, visit_time)

        return response

    return middleware

记录了最后一次到达或最后一次访问的时间。它不记录最后一次退出或“最后一次看到”的时间。

zyfwsgd6

zyfwsgd65#

I would go for django-last-seen
用法:

from last_seen.model import LastSeen

seen = LastSeen.object.when(user=user)
sxissh06

sxissh066#

与John Lehmann的中间件相同,但使用Andrew Swihart的建议重写为函数,并在Django 2.2上测试:

def last_user_activity_middleware(get_response):

    def middleware(request):

        response = get_response(request)

        key = "last-activity"

        if request.user.is_authenticated:

            last_activity = request.session.get(key)

            # If key is old enough, update database.
            too_old_time = timezone.now() - td(seconds=60 * 60)
            if not last_activity or parse(last_activity) < too_old_time:
                MyUser.objects.filter(email=request.user).update(
                    last_visit=timezone.now(),
                    login_count=F('login_count') + 1)

            request.session[key] = timezone.now().isoformat()

        return response

    return middleware

在官方文档中了解有关编写自己的中间件的更多信息:https://docs.djangoproject.com/en/2.2/topics/http/middleware/#writing-your-own-middleware

qoefvg9y

qoefvg9y7#

这是我的www.example.com文件,我已将其作为中间件添加到www.example.com文件中 lastvisitmiddleware.py file which I have added in the settings.py file as a middleware

from django.utils.timezone import now
from myapp.models import UserLastVisit
from django.contrib.auth.models import User

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

    def __call__(self, request):
        if request.user.is_authenticated:
            # Update last visit time after request finished processing.
            user = User.objects.get(id=request.user.id)
            userLastVisit = UserLastVisit.objects.filter(user_id=user)
            if userLastVisit:
                userLastVisit.update(last_visit=now())
            else:
                UserLastVisit.objects.create(user_id=user, last_visit=now())

        response = self.get_response(request)
        return response

setings.py file

MIDDLEWARE = [
   ...
   'mysite.lastvisitmiddleware.LastVisitMiddleware',
   ...
]

models.py

class UserLastVisit(models.Model):
    user_id = models.ForeignKey(User, models.DO_NOTHING, db_column='user_id')
    last_visit = models.DateTimeField()

这个解决方案对我很有效。现在,每次用户访问网站时,UserLastVisit表都会更新为最新的last_visit。其中一个问题是,如果用户在不同的页面之间移动,然后最后一次访问也将被更新。我们可以使用一个时间范围,如24小时或类似的东西,以更新它只有一次在该时间范围内。它结合了本主题中提供的答案中的多种方法

pbossiut

pbossiut8#

我提出了上面的另一个解决方案,使用缓存版本和过期日期来确定一个间隔,在这段时间内我们不想更新“last seen”值:

# middleware.py
from django.utils.timezone import now
from django.core.cache import cache

class LastActivityMiddleware:
    """Records last activity of a user. This is different of user.last_login
    because you can be inactive without been logged out"""

    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        """Records the timestamp of last activity of a user, every x minutes"""
        if request.user.is_authenticated:
            last_activity_timeout = 60*10  # 10min expiration
            user_last_activity_cache_key = f'{request.user.email}_last_activity'

            # in version 1 of cache key, there is an expiration date. This expiration date reprensent
            # the cooldown time for the last activity to be updated again, using the second version of the
            # key which store the latest activity timestamp value
            v2 = cache.get_or_set(user_last_activity_cache_key, now(), timeout=None, version=2)

            if not cache.get(user_last_activity_cache_key, version=1):
                cache.set(user_last_activity_cache_key, v2, timeout=last_activity_timeout, version=1)
                cache.set(user_last_activity_cache_key, now(), timeout=None, version=2)

        response = self.get_response(request)
        return response

相关问题