【问题标题】:Django: get last user visit dateDjango:获取上次用户访问日期
【发布时间】:2013-08-28 08:47:44
【问题描述】:

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

【问题讨论】:

标签: django


【解决方案1】:

示例模型:

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 行代码。

在文档中查看有关 Middlewarecustom user models 的更多信息(自 Django 1.5 起)

【讨论】:

  • 您必须在process_response 的末尾返回response 对象。虽然 django-last-seen 是一个打包的解决方案,但我选择了这个答案,因为使用 django-last-seen 我必须记住在每次请求时都致电 LastSeen.object.when。当然,我可以将LastSeen.object.when 放在中间件中,但是我还不如自己编写中间件。
【解决方案2】:

这是一个中间件,它将跟踪用户的最后活动并按时间间隔进行计数。使用间隔创建离散的“会话”,可以跟踪/计算这些“会话”,同时最大程度地减少对数据库的写入。

每次认证用户执行请求时,都会访问缓存以找到他们的 最后一个活动,然后用新的时间戳更新缓存。如果 活动至少有“间隔”时间的差距,那么它将更新 数据库时间戳。

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 确定什么构成了被视为新登录的非活动时间间隔。
  2. 这会更新一个“UserProfile”对象,我与我的用户对象 1:1,但您可以更新任何您喜欢的对象。
  3. 确保将其包含在 settings.MIDDLEWARE_CLASSES 中。
  4. 注意此中间件使用process_request 而不是process_response 否则取决于中间件顺序,APPEND_SLASH 可能导致request.user 不可用,如讨论:Django: WSGIRequest' object has no attribute 'user' on some pages?

【讨论】:

  • 我必须添加 from dateutil.parser import parse 并将 if not last_activity or last_activity &lt; too_old_time: 替换为 if not last_activity or parse(last_activity) &lt; too_old_time: .........并且............ request.session[self.KEY] = timezone.now() request.session[self.KEY] = timezone.now().isoformat()
  • 忘了说明原因:Django 会话显然需要 JSON 可序列化数据,而 timezone.now() 等日期时间则不需要,因此我不得不使用 datetime.isoformat() 将其存储为字符串并解析回到日期时间。
  • 我应该把它保存在 middleware.py 文件中吗?
【解决方案3】:

考虑到@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

【讨论】:

  • 但是如果我使用 JWT 应该怎么办呢?
【解决方案4】:

I would go for django-last-seen

用法:

from last_seen.model import LastSeen

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

【讨论】:

  • 感谢大家的快速解答。让我试试 django-last-seen out。 Python Fanboy 的回答可能就足够了,但我需要能够基于“模块”进行跟踪。
  • 看起来真不错!该软件包是否仍在工作和/或维护?
  • 此软件包不再适用于最新版本的 django。
【解决方案5】:

与 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

【讨论】:

    【解决方案6】:

    @John Lehmann 的解决方案很棒。但是,它需要使用特定的cache-based sessions 设置来避免每次请求都写入数据库。

    基于缓存的会话中有两个选项,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
    

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

    【讨论】:

      【解决方案7】:

      这是我在 settings.py 文件中作为中间件添加的 lastvisitmiddleware.py 文件

      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
      

      settings.py 文件

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

      这个解决方案对我有用。现在,每次用户访问该站点时,都会使用最新的 last_visit 更新 UserLastVisit 表。一个问题是,如果用户在不同页面之间旅行,那么最后一次访问也会被更新。我们可以使用 24 小时或类似的时间范围在该时间范围内只更新一次。它结合了此线程上可用答案中的多种方法

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-07-20
        • 1970-01-01
        • 2022-01-25
        • 2018-04-12
        • 2022-07-09
        • 1970-01-01
        • 2019-04-18
        相关资源
        最近更新 更多