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

DRF更新字段后接口返回旧数据需重启服务才生效问题排查

问题现象

发送PATCH请求更新Patient记录时,数据已成功写入数据库,Django Admin管理后台可查看到最新内容,但接口响应始终返回旧数据,必须重启服务后才能获取到更新后的字段值。

相关代码

Patient Model

class Patient(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )
    firstName = models.CharField(max_length=255)
    lastName = models.CharField(max_length=255)
    birthdate = models.DateField(auto_now_add=False, null=True, blank=True)
    nationalCode = models.CharField(max_length=10)
    address = models.TextField(blank=True)
    cellphone = models.CharField(max_length=15, blank=True)
    haveInsurance = models.BooleanField(default=False)
    insuranceNumber = models.CharField(max_length=20, blank=True)
    image = models.ImageField(null=True,blank=True, upload_to=patient_image_file_path)
    patientHistory = models.ForeignKey(
        'PatientHistory',
        on_delete=models.CASCADE,
        blank=True, null=True)

    def __str__(self):
        return self.firstName + ' ' + self.lastName

Patient ViewSet

class PatientViewSet(viewsets.ModelViewSet):
    """viewset for listing and creating patients"""
    serializer_class = serializers.PatientSerializer
    queryset = Patient.objects.all().order_by('-lastName')
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAdminUser | IsObjectOwner,)

    def get_queryset(self):
        firstName = self.request.query_params.get('first_name')
        lastName = self.request.query_params.get('last_name')
        insuranceNumber = self.request.query_params.get('insurance_number')
        cellphone = self.request.query_params.get('cellphone')
        queryset = self.queryset
        if firstName:
            queryset = queryset.filter(firstName=firstName)
        if lastName:
            queryset = queryset.filter(lastName=lastName)
        if insuranceNumber:
            queryset = queryset.filter(insuranceNumber=insuranceNumber)
        if cellphone:
            queryset = queryset.filter(cellphone=cellphone)
        return queryset
    

    def perform_create(self, serializer):
        if self.request.user.is_staff:
            email = self.request.data['email']
            cellphone = self.request.data['cellphone']
            name = self.request.data['firstName'] + " " + self.request.data['lastName']
            if get_user_model().objects.filter(email=email).exists():
                user = get_user_model().objects.get(email=email)
            else:
                user = get_user_model().objects.create_user(
                    email=email,
                    password=cellphone,
                    name=name
                    )
        else:
            user = self.request.user

        haveInsurance = True if 'insuranceNumber' in self.request.data else False
        user.is_patient = True
        user.save()
        serializer.save(
            user=user,
            haveInsurance=haveInsurance
        )

Patient Serializer

class PatientSerializer(serializers.ModelSerializer):
    """Serializer for Patient objects"""
    patientHistory = serializers.SerializerMethodField()
    class Meta:
        model = Patient
        fields = [
            'id',
            'firstName',
            'lastName',
            'birthdate',
            'nationalCode',
            'address',
            'cellphone',
            'haveInsurance',
            'insuranceNumber',
            'patientHistory',
            'image'
        ]
        read_only_fields = ['id']

    def get_patientHistory(self, obj):
        patientHistory = obj.patientHistory
        return patientHistory
根因定位

问题出在ViewSet的get_queryset方法实现上:

  • 你在类属性层面定义了queryset = Patient.objects.all().order_by('-lastName'),这个QuerySet实例是类级别全局共享的,在服务启动类加载时就会被创建。
  • get_queryset方法中直接queryset = self.queryset引用了这个共享的QuerySet实例。Django的QuerySet在第一次被迭代求值后,会把查询结果缓存到实例内部的_cache属性中,后续所有复用这个实例的操作都会直接读取缓存结果,不会重新查询数据库。
  • 第一次接口请求触发查询后,缓存就被填充,后续不管数据库里的数据怎么更新,接口都会返回缓存里的旧数据,直到服务重启,类重新加载、QuerySet实例重建,缓存才会被清空。
  • 不需要额外编写update方法,也不需要配置所谓的「数据实时重载」,本质就是QuerySet实例被全局共享导致的进程内内存缓存问题。
修复方案

修改get_queryset方法,每次请求都生成全新的QuerySet实例,不要复用类属性上的共享QuerySet:

def get_queryset(self):
    firstName = self.request.query_params.get('first_name')
    lastName = self.request.query_params.get('last_name')
    insuranceNumber = self.request.query_params.get('insurance_number')
    cellphone = self.request.query_params.get('cellphone')
    # 每次请求新建QuerySet实例,避免复用历史缓存
    queryset = Patient.objects.all().order_by('-lastName')
    if firstName:
        queryset = queryset.filter(firstName=firstName)
    if lastName:
        queryset = queryset.filter(lastName=lastName)
    if insuranceNumber:
        queryset = queryset.filter(insuranceNumber=insuranceNumber)
    if cellphone:
        queryset = queryset.filter(cellphone=cellphone)
    return queryset

如果要保留类属性的queryset定义(供DRF内置的权限检查、元数据解析等逻辑使用),可以在引用时调用.all()生成全新副本,同样可以避免缓存问题:

def get_queryset(self):
    # ... 省略参数获取逻辑
    queryset = self.queryset.all() # 生成无缓存的新QuerySet副本
    # ... 省略后续过滤逻辑

内容的提问来源于stack exchange,提问作者Ehsan Fouladi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 07:30:43