You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 07:24:28