Pydantic 2.14 的 Url 长度约束报 TypeError,改用 URL 专用约束
给 pydantic_core.Url 叠加 Field 长度约束可能触发无 len 的 TypeError。使用 AnyUrl 与 UrlConstraints 表达真实的 URL 限制,避免把删约束当成完成验证。
模型字段写成 Annotated[Url, Field(min_length=1, max_length=255)],输入看起来合法的地址,却直接抛出 TypeError,消息包含 object of type 'Url' has no len() 或 Unable to apply constraint 'min_length'。这里要核对导入的是 pydantic_core.Url,还是 Pydantic 提供的 URL 类型,以及约束是否适用于当前 schema。
Pydantic 2.14.1/core 2.50.1 有这一组合的明确记录。不能照搬早期 URL 长度问题的结论,把它当作“再加一个 __len__ 就能修复”的普通回归;对原生 URL schema 叠加不受支持的通用长度约束,不是可靠的校验契约。
用 UrlConstraints 表达 URL 的最大长度
如果要求“合法 URL,长度不超过 255”,可以按 URL 类型提供的约束声明:
from typing import Annotated
from pydantic import AnyUrl, BaseModel, UrlConstraints
LimitedUrl = Annotated[AnyUrl, UrlConstraints(max_length=255)]
class Request(BaseModel):
callback: LimitedUrl
这里使用 Pydantic 的 AnyUrl,长度约束进入 URL schema,而不是在已经构造好的底层对象上调用通用 len()。AnyUrl 允许多种协议;业务若只允许 HTTP 和 HTTPS,应另外在 UrlConstraints 中声明 allowed_schemes=["http", "https"]。这是一项接口限制,不能在迁移时无意加入或删除。
UrlConstraints 没有 min_length 参数。非空且符合 URL 语法,与“原始字符串至少若干字符”也不是一回事。不要为了保留旧声明的外观,给它传一个未支持的参数。
原始文本长度与规范化后的 URL 分开判断
若数据库限制的是实际保存的 URL 文本,应明确保存哪个表示。URL 验证可能补路径斜杠、规范化主机等;原始输入字符串的长度不一定等于 str(model.callback) 的长度。若另加字段验证器,应按业务要求检查对应表示,并在不符合时抛 ValueError,让错误归入字段验证,而不是截断字符串。
如果业务只想保存未经 URL 解析的文本,就应按字符串定义契约,再用适当的 URL 验证流程;不能仅把字段换成 str,同时默认此前的协议、主机或地址规范化规则仍然存在。
删除 Field(min_length=..., max_length=...) 可以消除这条失败路径,却可能让实际长度限制消失。只有确认那些限制本来不属于业务要求时,才能单纯删除。
修改后比较普通地址、超长地址、空字符串、非法协议和程序内部已经构造的 URL 对象,同时核对生成的 JSON Schema 与真正的保存值。正常输入应进入同一业务分支,非法输入应得到约定的 ValidationError,而不是转换为成功响应;API 客户端依赖的错误码若发生变化,也需同步确认。


