Python colander 的表单验证传统方案

1次阅读

colander验证失败需显式调用e.asdict()获取字段级错误;required=false不影响反序列化,missing=colander.drop才剔除缺失字段;自定义validator必须返回none或raise colander.invalid。

Python colander 的表单验证传统方案

colander 验证失败时默认不报具体字段错误

colander.Schema.deserialize() 验证表单数据,如果失败,抛出的是 colander.Invalid 异常,但默认不会直接告诉你哪个字段、为什么错——它只在异常对象.asdict() 方法里组织错误信息,而很多老项目直接 str(e) 或没 catch 就返回了 500。

  • 必须显式调用 e.asdict() 才能得到字段级错误字典,例如 {'email': 'Invalid email address.'}
  • 若用在 Web 框架(如 Pyramid),别依赖框架自动格式化;Pyramid 的 exc_info 默认不触发 asdict()
  • colander.Invalid.msg 是顶层错误消息,对嵌套结构基本没用,别试图从中 parse 字段名

required=False + missing=colander.drop 不等于“可选字段”

很多人以为设了 required=False,字段就真的可传可不传,结果发现前端没传该字段,后端反而报 KeyError 或验证跳过——问题出在 missingdefault 的配合逻辑上。

  • required=False 只影响序列化时是否报错,不影响反序列化(即 deserialize)时字段是否存在
  • 真正控制“字段缺失时怎么处理”的是 missing:设为 colander.drop 才会从输入字典中彻底剔除该键;设为 colander.NULLNone 会保留空值并继续走后续验证
  • 常见踩坑:字段类型是 colander.Integer(),又设 missing=colander.drop,但没配 default,导致后续业务代码访问该字段时报 KeyError

自定义 validator 函数里不能 raise Exception

colander.function 或给字段加 validator= 参数时,习惯性用 raise ValueError('xxx'),结果验证失败却静默通过或抛出未捕获异常——colander 要求 validator 必须严格返回 None 表示通过,否则必须 raise colander.Invalid 实例。

  • 错误写法:Lambda node, value: raise ValueError('too long') → 触发未处理异常,中断整个验证流程
  • 正确写法:lambda node, value: None if len(value)
  • 注意 node 是字段节点对象,不是字符串名;传错 node 会导致错误信息里字段路径为空或错乱

colander.MappingSchema 对 null 输入的容忍度极低

前端偶尔发来 null(JSON 中的 nullpython 中是 None),而 schema 是 colander.MappingSchema,结果直接炸出 TypeError: argument of type 'NoneType' is not iterable ——这不是 bug,是设计使然:MappingSchema 默认不接受 None 作为输入值。

立即学习Python免费学习笔记(深入)”;

  • 解决方式只有两个:要么在调用 deserialize() 前预检输入是否为 None,要么给 schema 显式加 typ=colander.Mapping(allow_empty=True)(注意:allow_empty 控制的是空 dict,不是 None
  • 更稳妥的做法是包装一层:如果原始数据是 None,先转成空字典 {} 再进 deserialize
  • 这个行为和 colander.SequenceSchema 一致,但容易被忽略,因为多数文档示例都从非空字典开始

事情说清了就结束。colander 的验证逻辑看似简单,但字段缺失处理、null 输入、validator 抛错规范这三处,几乎每个用过的人都至少掉过一次坑。

text=ZqhQzanResources