bigywater.tech
Django · 63

Field Validator 与自定义校验器

给字段加 validators 就能保证数据库里的值合法吗?不能。这篇验证校验器在 save()、full_clean()、ModelForm 里分别运行不运行,以及自定义校验器怎么写才规范。

save() 不校验
validators 违反时 save() 照样成功;只有 full_clean() / ModelForm 才检查
错误累加
一个字段的所有 validators 都会运行,错误一起返回
deconstructible
可配置的校验器类要加它,迁移才能序列化

save() vs full_clean()

实测 · 本机执行输出
save() 成功,库里存了: ROOT!!! 999 (既违反正则也违反 MaxValue,save() 没有检查)
full_clean() → {'name': ['letters'], 'age': ['max_value']}

Django 认为「校验」是表单/序列化层的事,模型层只提供 full_clean() 让你主动调用。所以通过 shell、管理命令、objects.create() 写入的数据不会被校验器挡住。真正的数据完整性要靠数据库约束(CheckConstraint、UniqueConstraint,本文没有测)。

validators 的运行

实测 · 本机执行输出
run_validators('root') 全部错误: [('reserved', '用户名 root 被保留')]
run_validators('ROOT12345') 全部错误: [('letters', '只能小写字母'), ('max_length', 'Ensure this value has at most 8 characters (it has 9).')]
age=200: [('max_value', {'limit_value': 150, 'show_value': 200, 'value': 200})]
name 字段的 validators 列表(自定义 + 自动加入的 max_length): ['not_forbidden', 'RegexValidator', 'MaxLengthValidator']

一个校验器抛 ValidationError 后,同字段的其它校验器仍会执行,所有错误合并返回。

ModelForm 与 unique

实测 · 本机执行输出
is_valid() = False
{'name': ['letters'], 'age': ['max_value']}
表单字段自带的校验器: []
实测 · 本机执行输出
full_clean(): {'name': ['unique']}
save() 直接: IntegrityError

ModelForm 把模型校验接进了表单校验。validate_unique 先查再写,两个并发请求仍可能都通过,最终由数据库的唯一约束拒绝其中一个(第 35 篇)。

可配置的校验器

实测 · 本机执行输出
deconstruct(): ('__main__.MaxWords', (3,), {})
校验: max_words {'n': 2}

校验器实例要能比较相等(__eq__)并可序列化,否则每次 makemigrations 都会认为字段变了。

分步演示:validators 在哪里运行

validators
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。SQLite
    • 没有验证:validators 与 clean() 中 ValidationError 的 params 国际化、Model.clean() / clean_fields() 的调用顺序、CheckConstraint 的替代方案、DRF 序列化器里的校验器
    京ICP备2026067502号-1京公网安备11010502063487号