148 lines
5.2 KiB
Markdown
148 lines
5.2 KiB
Markdown
# WTForms Schema 规范
|
||
|
||
## 概述
|
||
|
||
Schema 层负责请求数据的校验。项目使用 `WTForms` 定义表单类,通过 `validators` 约束字段格式,框架在接收请求体时自动执行校验。
|
||
|
||
:::tip 设计原则
|
||
- 创建/编辑共用一个表单类,继承 `BaseForm`
|
||
- 特殊操作单独建类(如状态更新、排序调整)
|
||
- `message` 必填,用于返回给前端的错误信息
|
||
- 唯一性校验通过自定义 `validate_` 方法实现
|
||
:::
|
||
|
||
## BaseForm 继承
|
||
|
||
以岗位模块为例,`src/modules/system/position/schema.py` 完整内容:
|
||
|
||
```python
|
||
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')
|
||
])
|
||
```
|
||
|
||
## Validator 选择表
|
||
|
||
| Validator | 作用 | 示例 |
|
||
|:----------|:-----|:-----|
|
||
| `DataRequired` | 必填字段,不允许空值 | `DataRequired(message='不能为空')` |
|
||
| `Length` | 字符串长度限制 | `Length(min=1, max=150)` |
|
||
| `NumberRange` | 数值范围限制 | `NumberRange(min=1, max=2)` |
|
||
| `Optional` | 可选字段 | `Optional()` |
|
||
| `Email` | 邮箱格式 | `Email(message='邮箱格式不正确')` |
|
||
| `URL` | URL 格式 | `URL(message='URL格式不正确')` |
|
||
| `Regexp` | 正则表达式 | `Regexp(r'^[a-zA-Z]+$')` |
|
||
|
||
## 自定义校验器
|
||
|
||
### 唯一性校验
|
||
|
||
```python
|
||
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('岗位名称已存在')
|
||
```
|
||
|
||
### 跨字段校验
|
||
|
||
```python
|
||
class DateRangeForm(BaseForm):
|
||
"""日期范围表单"""
|
||
start_date = DateField('开始日期', validators=[DataRequired()])
|
||
end_date = DateField('结束日期', validators=[DataRequired()])
|
||
|
||
def validate_end_date(self, field):
|
||
"""自定义校验:结束日期必须大于开始日期"""
|
||
if field.data < self.start_date.data:
|
||
raise ValidationError('结束日期必须大于开始日期')
|
||
```
|
||
|
||
## 在 Router 中使用
|
||
|
||
```python
|
||
from flask import request
|
||
from src.common.utils.request import parse_request_body
|
||
|
||
@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)
|
||
```
|
||
|
||
## 错误响应格式
|
||
|
||
校验失败时,框架自动返回标准错误响应:
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"data": null,
|
||
"msg": "岗位名称不能为空",
|
||
"ok": false
|
||
}
|
||
```
|
||
|
||
## 开发要点
|
||
|
||
1. **创建/编辑共用一个表单类**:继承 `BaseForm`,利用其内置的可选 `id` 字段
|
||
2. **特殊操作单独建类**:如状态更新、排序调整等,只包含必要的字段
|
||
3. **合理设置 Validator**:`DataRequired` / `Length` / `NumberRange` 等,让框架自动校验
|
||
4. **message 必填**:用于返回给前端的错误信息,方便调试
|
||
5. **唯一性校验**:通过自定义 `validate_` 方法实现,使用 Repository 层的 `exists_by` 方法
|
||
6. **字段名与前端一致**:确保前后端字段名对应
|
||
|
||
## 总结
|
||
|
||
WTForms Schema 通过 `BaseForm` 继承公共字段(id 等),使用 `validators` 声明约束,自定义 `validate_` 方法实现唯一性校验。特殊操作单独建类,`message` 必填用于返回给前端的错误信息。
|