# 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` 必填用于返回给前端的错误信息。