在Django中,我们可以使用Auth.User.last_login
获取用户上次登录的时间。仅当用户使用其用户名/密码登录时才会更新。假设用户已经登录并且身份验证信息保存在cookie中,因此无需登录即可访问该站点。我们如何获取用户以前访问过该站点的日期?这对于获取自上次访问以来获取新记录数量等查询非常有用。
答案 0 :(得分:28)
示例模型:
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
将新的中间件添加到Your settings.py:
MIDDLEWARE_CLASSES = (
...
'path.to.your.SetLastVisitMiddleware',
...
)
警告:未经过测试,但不需要安装外部软件包,只需要5行代码。
在Middleware和custom user models的文档中查看更多内容(自Django 1.5开始)
答案 1 :(得分:12)
这是一个中间件,它将跟踪用户上次活动和按时间间隔分隔的计数。使用间隔创建离散的"会话"可以跟踪/计算,同时最大限度地减少对数据库的写入。
每次auth用户执行请求时,都会点击缓存来查找它们 最后一个活动,然后使用新的时间戳更新缓存。如果 活动间隙至少为#34;间隔"时间,那么它会更新 数据库时间戳。
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
评论:
settings.LAST_ACTIVITY_INTERVAL_SECS
确定什么构成被视为新登录的非活动的间隔。settings.MIDDLEWARE_CLASSES
。process_request
而不是process_response
,否则,根据中间件订单,APPEND_SLASH
可能会导致request.user
无法使用,如上所述:Django: WSGIRequest' object has no attribute 'user' on some pages? 答案 2 :(得分:2)
I would go for django-last-seen
用法:
from last_seen.model import LastSeen
seen = LastSeen.object.when(user=user)
答案 3 :(得分:2)
考虑到@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
答案 4 :(得分:1)
与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
答案 5 :(得分:0)
@John Lehmann的解决方案很棒。但是,它需要使用特定的cache-based sessions设置来避免在每个请求上写入数据库。
基于缓存的会话中有两个选项,backends.cache
或backends.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
记录最后一次到达或最后一次访问的时间。它不记录最后一次退出或“最后一次看到”的时间。
答案 6 :(得分:0)
这是我的lastvisitmiddleware.py文件,我已将它作为中间件添加到settings.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
setings.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()
此解决方案对我有用。现在,每次用户访问该站点时,UserLastVisit表将使用最新的last_visit更新。一个问题是,如果用户在不同页面之间旅行,那么最后一次访问也会被更新。我们可以使用24小时之类的时间范围或类似的时间范围,在该时间范围内仅对其进行一次更新。它结合了该线程可用答案中的多种方法