如何让Django登录日志Middleware仅在用户登录时执行而非每次API调用?
问题:Django登录日志中间件仅在用户登录时执行一次的修改方案
我创建了一个用于记录用户登录活动日志的Django Middleware,但它会在每次API调用时执行,其中使用ActivityLog模型记录日志条目。
原Middleware代码:
class UserActivityMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # Set a flag in the session to indicate that the user has been logged in during this session request.session['user_logged_in'] = request.session.get('user_logged_in', False) response = self.get_response(request) # Check if the user is authenticated, has just logged in, and hasn't been logged during this session if ( request.user.is_authenticated and request.user.last_login is not None and request.session.get('user_logged_in', False) is False and not request.path.startswith('/static/') and not request.path.startswith('/media/') ): print("reached middleware") print(f"User: {request.user.username}") print(f"Last Login: {request.user.last_login}") print(f"Session Flag: {request.session.get('user_logged_in')}") print(f"Request Path: {request.path}") # Log the user login activity ActivityLog.objects.create( user=request.user, action="Login", target_model="User", target_object_id=request.user.id, details=f"User {request.user.username} logged in at {timezone.now()}." ) # Set a flag in the session to indicate that the user has been logged in during this session request.session['user_logged_in'] = True return response
请问我该如何修改,才能让它仅在用户登录时执行一次?
修改方案
方法一:监听Django登录信号(推荐)
直接用Django内置的user_logged_in信号,这是最精准的方式——只有用户成功完成登录动作时才会触发,完全避免中间件遍历所有请求的问题:
# 在你的app目录下新建signals.py文件 from django.contrib.auth.signals import user_logged_in from django.dispatch import receiver from django.utils import timezone from .models import ActivityLog @receiver(user_logged_in) def log_user_login(sender, request, user, **kwargs): ActivityLog.objects.create( user=user, action="Login", target_model="User", target_object_id=user.id, details=f"User {user.username} logged in at {timezone.now()}." )
然后在app的apps.py中注册信号:
from django.apps import AppConfig class YourAppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'your_app_name' # 替换成你的实际app名称 def ready(self): import your_app_name.signals # 对应上面的signals.py路径
方法二:优化中间件判断逻辑
如果坚持使用中间件,需要精准锁定「登录请求」场景,避免所有请求都进入判断流程:
class UserActivityMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 记录请求前的用户认证状态 was_authenticated_before = request.user.is_authenticated # 获取会话中已有的登录标记 session_logged_in = request.session.get('user_logged_in', False) response = self.get_response(request) # 仅当以下条件全部满足时,才记录登录日志: # 1. 请求前未认证,请求后已认证(说明刚完成登录) # 2. 当前会话还未记录过登录日志 # 3. 请求路径是登录接口(替换成你的实际登录路径,比如'/api/auth/login/') if ( not was_authenticated_before and request.user.is_authenticated and not session_logged_in and request.path == '/accounts/login/' and not request.path.startswith('/static/') and not request.path.startswith('/media/') ): ActivityLog.objects.create( user=request.user, action="Login", target_model="User", target_object_id=request.user.id, details=f"User {request.user.username} logged in at {timezone.now()}." ) request.session['user_logged_in'] = True return response
原代码问题说明
原代码的核心问题是没有精准锁定「用户刚刚完成登录」的动作:虽然会话标记能避免重复创建日志,但中间件会在每一次请求时都执行判断逻辑。而且原代码在请求开始时就重置user_logged_in标记的操作冗余,反而可能导致逻辑混乱。
内容的提问来源于stack exchange,提问作者Rahul Sharma
相关产品推荐
相关产品推荐

