Django-online-users统计在线用户仅显示管理员问题排查
问题根因
问题由三个核心逻辑错误共同导致:
- 视图集的
queryset被定义为类属性,会在服务启动、视图类加载时执行一次查询,后续所有请求都复用这个静态的旧查询结果,不会实时拉取最新120秒的活跃用户数据。 - 序列化器模型不匹配:
OnlineUsersSerializer绑定的是Profile模型,但查询集返回的是OnlineUserActivity模型实例,字段完全不对应,会导致非管理员用户的数据无法正常序列化输出。 django-online-users默认适配Django原生Session认证,对JWT认证场景兼容不足:一是JWT登录不会触发Django原生的user_logged_in信号,默认的登录追踪逻辑不生效;二是JWT请求无有效session_key,调用update_user_activity时如果不传唯一标识,会导致活跃记录写入失败或关联错误。
修复步骤
1. 修正视图集查询逻辑,每次请求实时拉取最新数据
移除类属性上的静态queryset,重写get_queryset方法,保证每次请求都查询当前时间点前120秒的活跃记录:
from datetime import timedelta from rest_framework import viewsets from online_users.models import OnlineUserActivity from .serializers import OnlineUsersSerializer class OnlineUsers(viewsets.ModelViewSet): serializer_class = OnlineUsersSerializer def get_queryset(self): return OnlineUserActivity.get_user_activities(timedelta(seconds=120))
2. 重写序列化器,匹配正确模型并嵌套返回Profile数据
OnlineUserActivity模型仅存储关联用户、最后活跃时间、追踪标识三个核心字段,需要嵌套关联查询用户的Profile信息,不要直接绑定Profile模型:
from rest_framework import serializers from .models import Profile from online_users.models import OnlineUserActivity class ProfileBriefSerializer(serializers.ModelSerializer): class Meta: model = Profile fields = ['city', 'country', 'bio', 'profile_pic'] class OnlineUsersSerializer(serializers.ModelSerializer): profile = ProfileBriefSerializer(source='user.profile', read_only=True) username = serializers.CharField(source='user.username', read_only=True) last_activity = serializers.DateTimeField(read_only=True) class Meta: model = OnlineUserActivity fields = ['id', 'username', 'profile', 'last_activity']
3. 优化活跃记录追踪Mixin,兼容JWT认证场景
原Mixin没有处理JWT请求无session的问题,补充唯一追踪标识生成逻辑,避免记录写入失败:
import uuid from online_users.models import OnlineUserActivity class OnlineNowMixin: def initial(self, request, *args, **kwargs): super().initial(request, *args, **kwargs) user = request.user if not user.is_authenticated: return # 区分session认证和JWT认证,生成不同的唯一追踪标识 if hasattr(request, 'session') and request.session.session_key: track_key = request.session.session_key else: # JWT场景生成独立key,避免和session记录冲突 track_key = f"jwt_{user.id}_{uuid.uuid4().hex[:6]}" OnlineUserActivity.update_user_activity( user=user, session_key=track_key )
4. 补充JWT登录时的初始活跃记录写入
JWT登录不会触发Django原生登录信号,需要在登录接口主动写入第一次活跃记录:
from rest_framework_simplejwt.views import TokenObtainPairView from .serializers import CustomTokenObtainPairSerializer from online_users.models import OnlineUserActivity class CustomTokenObtainPairView(TokenObtainPairView): serializer_class = CustomTokenObtainPairSerializer def post(self, request, *args, **kwargs): response = super().post(request, *args, **kwargs) # 登录成功后主动更新活跃状态 if hasattr(request, 'user') and request.user.is_authenticated: OnlineUserActivity.update_user_activity( user=request.user, session_key=f"jwt_login_{request.user.id}" ) return response
5. 配置校验
- 确认所有需要统计用户活跃的业务视图都继承了
OnlineNowMixin - 确认DRF配置中JWT认证类已正确加入
DEFAULT_AUTHENTICATION_CLASSES列表,请求到达Mixin时request.user已经是认证后的用户对象 - 测试时分别用Session登录的管理员、JWT登录的普通用户触发任意接口请求,2分钟内调用在线用户接口即可看到全部在线用户数据
内容的提问来源于stack exchange,提问作者Emm
相关产品推荐
相关产品推荐

