Files
djangoadmin-wiki/DjangoAdmin_Flask_NaiveVue/zh/volume7/pydantic.md
T
2026-09-12 23:12:15 +08:00

6.5 KiB

WTForms Schema规范

概述

项目使用 WTForms 进行请求数据校验。所有请求体参数通过 WTForms 表单类定义,框架在调用 Router 前自动完成解析和验证。

:::tip 核心原则

  • 请求体参数必须声明为 WTForms 表单类(Form 类)
  • 字段约束使用 validators 定义
  • 自定义校验使用 validate_ 方法
  • 唯一性校验在 Schema 中查询数据库 :::

命名规范

类型 命名格式 示例
创建/编辑表单 {模块}Form PositionForm
状态更新表单 {模块}StatusForm PositionStatusForm
查询参数表单 {模块}QueryForm PositionQueryForm

:::tip 继承 BaseForm 创建/编辑表单继承 src.common.forms.base_form.BaseForm,自动获得 id 字段(编辑时必填)。 :::

PositionForm 示例

以岗位模块为例,src/modules/system/position/schema.py 完整内容:

from wtforms import StringField, IntegerField
from wtforms.validators import DataRequired, Length, NumberRange

from src.common.forms.base_form import BaseForm


class PositionForm(BaseForm):
    """岗位创建/编辑表单"""
    name = StringField('岗位名称', validators=[
        DataRequired(message='岗位名称不能为空'),
        Length(min=1, max=150, message='岗位名称长度必须在1-150个字符之间')
    ])
    status = IntegerField('岗位状态', validators=[
        DataRequired(message='岗位状态不能为空'),
        NumberRange(min=1, max=2, message='岗位状态必须是1或2')
    ])
    sort = IntegerField('岗位排序', validators=[
        DataRequired(message='岗位排序不能为空'),
        NumberRange(min=0, max=99999, message='岗位排序必须在0-99999之间')
    ])


class PositionStatusForm(BaseForm):
    """岗位状态更新表单"""
    id = IntegerField('岗位ID', validators=[
        DataRequired(message='岗位ID不能为空'),
        NumberRange(min=1, message='岗位ID必须大于0')
    ])
    status = IntegerField('岗位状态', validators=[
        DataRequired(message='岗位状态不能为空'),
        NumberRange(min=1, max=2, message='岗位状态必须是1或2')
    ])

:::tip BaseForm 基类 BaseForm(定义在 src/common/forms/base_form.py)提供 id 字段和数据清洗功能,自动将前端传入的空字符串 ''/'null'/'undefined' 转为 None,避免 WTForms 解析报错。 :::

Validator 约束

Validator 类型 说明 示例
DataRequired any 必填字段 DataRequired(message='不能为空')
Length str 字符串长度 Length(min=1, max=150)
NumberRange int/float 数值范围 NumberRange(min=0, max=99999)
Optional any 可选字段 Optional()
Email str 邮箱格式 Email(message='邮箱格式不正确')
URL str URL 格式 URL(message='URL格式不正确')
Regexp str 正则表达式 Regexp(r'^[a-zA-Z]+$')

:::tip Validator 说明

  • DataRequired:必填字段,不允许空值
  • Length:字符串长度限制
  • NumberRange:数值范围限制
  • Optional:可选字段 :::

自定义校验器

唯一性校验

from wtforms import ValidationError
from src.modules.system.position.repository import PositionRepository


class PositionForm(BaseForm):
    """岗位创建/编辑表单"""
    name = StringField('岗位名称', validators=[
        DataRequired(message='岗位名称不能为空'),
        Length(min=1, max=150, message='岗位名称长度必须在1-150个字符之间')
    ])

    def validate_name(self, field):
        """岗位名称唯一性校验"""
        exclude_id = self.id.data if self.id.data else None
        if PositionRepository.exists_by([Position.name == field.data], exclude_id):
            raise ValidationError('岗位名称不能重复')

格式校验

from wtforms import ValidationError
import re


class UserForm(BaseForm):
    """用户创建/编辑表单"""
    username = StringField('用户名', validators=[
        DataRequired(message='用户名不能为空'),
        Length(min=3, max=50, message='用户名长度必须在3-50个字符之间')
    ])
    email = StringField('邮箱', validators=[
        DataRequired(message='邮箱不能为空'),
        Length(max=100, message='邮箱长度不能超过100个字符')
    ])
    phone = StringField('手机号', validators=[
        Optional(),
        Length(max=20, message='手机号长度不能超过20个字符')
    ])

    def validate_username(self, field):
        """用户名只允许字母、数字、下划线"""
        if not re.match(r'^[a-zA-Z0-9_]+$', field.data):
            raise ValidationError('用户名只允许字母、数字、下划线')

    def validate_email(self, field):
        """邮箱格式校验"""
        if not re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', field.data):
            raise ValidationError('邮箱格式不正确')

    def validate_phone(self, field):
        """手机号格式校验(可选字段,有值时校验)"""
        if field.data and field.data != '':
            if not re.match(r'^1[3-9]\d{9}$', field.data):
                raise ValidationError('手机号格式不正确')

:::tip validator 规则

  • 方法名必须为 validate_{field_name}
  • 校验失败抛出 ValidationError(错误消息)
  • 可选字段需判断 None 值 :::

在 Router 中使用

@position_blue.route('/add', methods=['POST'])
@check_permission('sys:position:add')
def add():
    """
    新增岗位
    """
    from src.modules.system.position.service import add_position
    from src.modules.system.position.schema import PositionForm
    data = parse_request_body(request, PositionForm)
    return add_position(data)

:::tip 数据访问 Service 层通过 data['field'] 直接访问字段。WTForms 在进入 Router 之前已完成校验,Service 无需重复验证。 :::

错误响应

WTForms 校验失败时,由框架自动转换:

{
    "code": 1,
    "data": null,
    "msg": "岗位名称不能为空",
    "ok": false
}

总结

WTForms Schema 规范要求:表单类命名 {Module}Form,继承 BaseForm;字段约束用 validators 定义长度/范围;自定义校验用 validate_{field_name} 方法抛 ValidationError;唯一性校验通过 Repository.exists_by(filters, exclude_id) 实现。框架自动解析和验证,Service 通过 form.data['field'] 访问已校验的数据。