Django Ninja中如何让Schema仅保留请求传入的键(含值为None的情况)
Django Ninja中如何让Schema仅保留请求传入的键(含值为None的情况)
这个问题我之前也碰到过,确实挺闹心的——Pydantic默认会把Schema里所有可选字段都初始化为None,不管请求里有没有传,这就导致我们没法区分「用户没传这个字段」和「用户主动传了null」。不过有几个实用的办法可以解决这个问题:
方法一:用exclude_unset=True替代exclude_none=True
这是最简单的解决方案,直接修改你序列化Schema时的参数:
原来的代码:
for key, value in filters.dict(exclude_none=True).items():
改成:
for key, value in filters.dict(exclude_unset=True).items():
原理说明:
exclude_none=True会移除所有值为None的键,不管这个键是用户传的还是Schema默认的;exclude_unset=True只会移除那些没有被请求显式设置过的键——也就是说,用户主动传了null的键会被保留,而Schema里默认存在但用户没传的键会被过滤掉。
比如你提到的请求:
{ "major_version": { "exact": null }, "app_name": { "icontains": "google" } }
用exclude_unset=True序列化后得到的字典会是:
{ "major_version": {"exact": None}, "app_name": {"icontains": "google"} }
这样你的循环就只会处理用户实际传入的键,包括那个值为None的exact,生成正确的Q(major_version__exact=None)查询条件。
方法二:用Undefined替代None作为字段默认值
如果需要更严格的控制(比如确保未传入的字段完全不出现Schema实例中),可以给Schema字段设置Undefined作为默认值:
首先导入Undefined:
from pydantic import Undefined
然后修改你的Schema:
class NumericalFilterSchema(Schema): gt: Optional[int] = Undefined lt: Optional[int] = Undefined gte: Optional[int] = Undefined lte: Optional[int] = Undefined exact: Optional[int] = Undefined class Config: extra = "forbid" class StringFilterSchema(Schema): contains: Optional[str] = Undefined icontains: Optional[str] = Undefined exact: Optional[str] = Undefined class Config: extra = "forbid"
原理说明:
- 当字段默认值是
Undefined时,Pydantic不会自动初始化这个字段,只有当用户主动传入(包括传null)时,该字段才会出现在Schema实例中; - 此时你甚至不需要额外加
exclude_unset=True,直接调用filters.dict()就能得到仅包含用户传入字段的字典。
这个方法更严谨,但需要修改所有Schema字段的默认值,适合对字段存在性要求严格的场景。
额外注意事项
如果你使用的是Pydantic v2(Django Ninja最新版本一般基于Pydantic v2),dict()方法已经被model_dump()替代,参数用法是一样的:
filters.model_dump(exclude_unset=True)
这样修改后,你就能准确区分用户没传的字段和主动传null的字段,生成符合预期的动态查询啦!
备注:内容来源于stack exchange,提问作者Leemosh
相关产品推荐
相关产品推荐

