Django Rest Framework 动态默认值问题

问题背景

一个使用Django Rest Framework项目其中的一个查询页面,按照起始日期和终止日期范围查询。其中起始日期通过前端转递,终止日期通过默认值自动生成,默认值为今天。项目启动时验证功能正常。但是几天后发现查询不到近期的数据,当天和当天附近的数据无法查询到。

复现方法

为了复现和暴露问题方便,这里使用DateTimeField来代替DateField。因此可以看到时间信息。

下面只贴出和问题相关的代码:

serializers/rest_serializer.py

from rest_framework import serializers

import datetime


class RestSerializer(serializers.Serializer):
    from_time = serializers.DateTimeField()
    # 这里看出问题了吗?
    to_time = serializers.DateTimeField(default=datetime.datetime.today())
    

该文件中,to_time字段指定了默认值,该默认值是动态的。开发者期待每次请求的时候默认值都为当前的时刻。

views/rest_view.py

from rest_framework.decorators import api_view
from demo_app.serializers.rest_serializer import RestSerializer
from django.http import HttpResponse
import json
import datetime


@api_view(['GET'])
def rest_view(request):
    from_time = datetime.datetime.strptime(request.GET['from_time'], '%Y%m%dT%H:%M:%S')
    serializer = RestSerializer({'from_time':from_time})
    print(serializer.data)
    return HttpResponse(json.dumps(serializer.data))
    

为了演示方便将serializer接收的值返回给浏览器。

启动Django测试服务器后,使用浏览器访问:http://127.0.0.1:8000/demo?from_time=20201010T20:00:00,看到页面返回如下:

{"from_time": "2020-10-10T20:00:00Z", "to_time": "2025-11-06T02:49:50.646458Z"}

反复刷新浏览器,发现to_time的值不会发生变化。我们期待的动态默认值完全没有起作用。背景中的问题已经复现。

问题原因

我们可以尝试重启Django测试服务器,再刷新浏览器,发现to_time字段会更新。但之后再反复刷新,字段值都不会更新。可以得出结论:按照当前的写法,所谓的动态默认值只会在服务启动的时候指定,之后就类似于静态默认值,再也不会得到更新。和问题背景中提到的现象完全吻合。

解决方式

经查阅相关资料,发现default还可以接受函数类型。按照这个思路,修改serializers/rest_serializer.py为:

from rest_framework import serializers

import datetime


class RestSerializer(serializers.Serializer):
    from_time = serializers.DateTimeField()
    # 注意,这里相比复现方法一节的代码,删除了today后面的括号,即传入的是datetime.datetime.today方法本身
    to_time = serializers.DateTimeField(default=datetime.datetime.today)
    

再次重启Django测试服务器,反复刷新浏览器,发现每次刷新,to_time的值都会更新。问题得到解决。

总结

DRF框架的默认值赋值仅在项目启动的时候执行。遇到动态默认值这种情况,如果我们使用函数调用,则启动时函数调用的返回值会被当做默认值传入,和使用静态默认值(直接写常量)没有事实上的区别。如果要使用真正的动态默认值,需要在指定默认值的地方传入函数本身。两种写法仅有一对括号的区别,但结果相差甚远,并且造成的问题较为隐晦难以排查。因此撰写本文作为警示。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容