Skip to content

WTForms Schema ​

Schema 层负责请求数据的校验。项目使用 WTForms 定义表单类,通过 validators 约束字段格式,框架在接收请求体时自动执行校验。

文件位置 ​

src/modules/system/position/schema.py

完整代码 ​

以下为 src/modules/system/position/schema.py 的核心代码(省略版权头):

python
"""
岗位表单验证模块
=============================================================

提供岗位相关的表单验证功能,包括:
- 岗位添加/更新表单验证
- 岗位状态设置表单验证
"""

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

from src.common.forms.base_form import BaseForm
from src.modules.system.position.repository import PositionRepository


# ============================================================
# 岗位表单验证(添加和更新共用)
# ============================================================

class PositionForm(BaseForm):
    """
    岗位表单验证类

    用于添加和更新岗位时的数据验证。
    添加时 id 为 None,更新时 id 必须有值。
    """

    # 岗位ID:更新时必填,添加时可选
    id = IntegerField(
        label='岗位ID',
        validators=[
            Optional(),  # 允许为空(添加时)
            NumberRange(min=1, message='岗位ID必须大于0')
        ],
        description='岗位唯一标识,更新时必填,添加时自动生成'
    )

    # 岗位名称:必填,最长150字符,不可重复
    name = StringField(
        label='岗位名称',
        validators=[
            DataRequired(message='岗位名称不能为空'),
            Length(max=150, message='岗位名称长度不得超过150个字符')
        ],
        description='岗位显示名称,如:技术总监、产品经理'
    )

    # 岗位状态:必填,1-在用,2-停用
    status = IntegerField(
        label='岗位状态',
        validators=[
            DataRequired(message='岗位状态不能为空'),
            NumberRange(min=1, max=2, message='岗位状态值在1~2之间')
        ],
        description='岗位状态:1-在用,2-停用'
    )

    # 岗位排序:可选,0-99999之间的整数
    sort = IntegerField(
        label='岗位排序',
        validators=[
            Optional(),
            NumberRange(min=0, max=99999, message='岗位排序值在0~99999之间')
        ],
        description='排序值,数值越小越靠前'
    )

    # ============================================================
    # 自定义验证方法
    # ============================================================

    def validate_name(self, field):
        """
        自定义验证:岗位名称不能重复

        验证逻辑:
            1. 添加时:检查数据库中是否存在同名记录
            2. 更新时:排除当前记录自身,检查其他记录是否同名
        """
        if not field.data:
            return

        # 获取当前记录的ID(更新时存在,添加时为None)
        current_id = self.id.data if self.id.data else None

        # 唯一性校验(软删过滤 + 更新时排除自身)
        if PositionRepository.exists_by({'name': field.data}, exclude_id=current_id):
            raise ValidationError("岗位名称已存在")


# ============================================================
# 状态设置表单
# ============================================================

class PositionStatusForm(BaseForm):
    """岗位状态设置表单验证类"""

    id = IntegerField(
        label='岗位ID',
        validators=[
            DataRequired(message='岗位ID不能为空'),
            NumberRange(min=1, message='岗位ID必须大于0')
        ],
        description='要修改状态的岗位ID'
    )

    status = IntegerField(
        label='岗位状态',
        validators=[
            DataRequired(message='岗位状态不能为空'),
            NumberRange(min=1, max=2, message='岗位状态值在1~2之间')
        ],
        description='目标状态:1-在用,2-停用'
    )

代码解析 ​

基类选择 ​

基类用途说明
BaseForm创建/编辑通用表单继承 flask_wtf.FlaskForm,支持 ArrayField 处理
PositionStatusForm状态更新专用只包含 id 和 status 两个字段

id 字段 ​

创建和编辑共用 PositionForm:创建时不传 id,编辑时传入 id。id 字段定义为 Optional(),创建时可省略:

python
id = IntegerField(
    label='岗位ID',
    validators=[Optional(), NumberRange(min=1, message='岗位ID必须大于0')],
    description='岗位唯一标识,更新时必填,添加时自动生成'
)

Validator 约束 ​

Validator含义示例
DataRequired必填字段,不允许空值DataRequired(message='岗位名称不能为空')
Length(max=150)字符串长度限制Length(max=150, message='...')
NumberRange(min=1, max=2)数值范围限制NumberRange(min=1, max=2, message='...')
Optional可选字段Optional()

自定义校验器 ​

通过定义 validate_{field_name} 方法实现自定义校验:

python
def validate_name(self, field):
    """自定义验证:岗位名称不能重复"""
    if not field.data:
        return
    current_id = self.id.data if self.id.data else None
    if PositionRepository.exists_by({'name': field.data}, exclude_id=current_id):
        raise ValidationError("岗位名称已存在")
  • 方法名必须为 validate_{field_name}
  • 校验失败抛出 ValidationError(错误消息)
  • 唯一性校验通过 Repository.exists_by(filters, exclude_id) 实现,自动过滤软删除记录

如何在 Router 中使用 ​

python
from flask import Blueprint, request
from src.common.utils.request import parse_request_body
from src.modules.system.position import service as position
from src.core import response as R

position_blue = Blueprint('position', __name__, url_prefix='/position')


@position_blue.route('/add', methods=['POST'])
def add():
    """新增岗位"""
    json_data, err = parse_request_body()
    if err:
        return R.failed(msg=err)
    position.add_position(json_data)
    return R.ok(msg="添加成功")

校验失败时,由框架自动返回标准错误响应:

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

开发要点 ​

  1. 创建/编辑共用一个表单类:继承 BaseForm,显式定义 id 字段(Optional)
  2. 特殊操作单独建类:如状态更新,只包含必要的字段
  3. 合理设置 Validator:DataRequired / Length / NumberRange 等,让框架自动校验
  4. message 必填:用于返回给前端的错误信息,方便调试
  5. 唯一性校验:通过自定义 validate_ 方法实现,使用 Repository 层的 exists_by(filters, exclude_id) 方法
  6. 可选字段用 Optional():如 sort 排序字段,不传时使用数据库默认值

总结 ​

WTForms Schema 通过 BaseForm 继承公共能力,使用 validators 声明约束,自定义 validate_ 方法实现唯一性校验。唯一性校验使用 Repository.exists_by({'field': value}, exclude_id=current_id) 方法,自动过滤软删除记录并支持排除自身。特殊操作单独建类,message 必填用于返回给前端的错误信息。

小蚂蚁云团队 · 提供技术支持