Files
djangoadmin-wiki/DjangoAdmin_Flask_EleVue/zh/volume2/schema.md
T
2026-09-12 23:12:15 +08:00

187 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WTForms Schema
Schema 层负责请求数据的校验。项目使用 `WTForms` 定义表单类,通过 `validators` 约束字段格式,框架在接收请求体时自动执行校验。
## 文件位置
```
src/modules/system/position/schema.py
```
## 完整代码
```python
from wtforms import StringField, IntegerField
from wtforms.validators import DataRequired, Length, NumberRange, Optional
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')
])
```
## 代码解析
### 基类选择
| 基类 | 用途 | 包含字段 |
|:----|:----|:----|
| `BaseForm` | 创建/编辑通用表单 | 自带可选 `id` 字段(`Optional`),编辑时传入 |
| 特定操作表单 | 无额外字段,按需定义 |
创建和编辑共用 `PositionForm`:创建时不传 `id`,编辑时传入 `id`。`BaseForm` 的 `id` 字段定义为 `IntegerField('ID', validators=[Optional()])`,创建时可省略。
### Validator 约束
```python
name = StringField('岗位名称', validators=[
DataRequired(message='岗位名称不能为空'),
Length(min=1, max=150, message='岗位名称长度必须在1-150个字符之间')
])
```
| Validator | 含义 |
|:----|:----|
| `DataRequired` | 必填字段,不允许空值 |
| `Length(min=1, max=150)` | 字符串长度限制 |
| `NumberRange(min=1, max=2)` | 数值范围限制 |
| `Optional` | 可选字段 |
```python
status = IntegerField('岗位状态', validators=[
DataRequired(message='岗位状态不能为空'),
NumberRange(min=1, max=2, message='岗位状态必须是1或2')
])
```
| 参数 | 含义 |
|:----|:----|
| `min=1` | 最小值(大于等于) |
| `max=2` | 最大值(小于等于) |
| `message` | 校验失败时的错误信息 |
```python
sort = IntegerField('岗位排序', validators=[
DataRequired(message='岗位排序不能为空'),
NumberRange(min=0, max=99999, message='岗位排序必须在0-99999之间')
])
```
排序字段允许 0 到 99999 的范围。
### 状态更新单独建类
```python
# ============================================================
# 设置状态表单类
# ============================================================
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')
])
```
状态更新接口只需要 `id` 和 `status` 两个字段,与创建/编辑表单分开定义,接口语义更清晰。
## 如何在 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="添加成功")
```
`parse_request_body` 函数自动解析请求体 JSON。校验失败时,Service 层抛出 `BizError`,由异常处理器自动返回标准错误响应:
```json
{
"code": 1,
"data": null,
"msg": "岗位名称不能为空"
}
```
## 自定义校验器
如果需要自定义校验逻辑,可以在表单类中定义 `validate_` 开头的方法:
```python
class PositionForm(BaseForm):
"""岗位创建/编辑表单"""
name = StringField('岗位名称', validators=[
DataRequired(message='岗位名称不能为空'),
Length(max=150, message='岗位名称长度不得超过150个字符')
])
def validate_name(self, field):
"""自定义校验:检查岗位名称是否已存在"""
from src.modules.system.position.repository import PositionRepository
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('岗位名称已存在')
```
## 开发要点
1. **创建/编辑共用一个表单类**:继承 `BaseForm`,利用其内置的可选 `id` 字段
2. **特殊操作单独建类**:如状态更新、排序调整等,只包含必要的字段
3. **合理设置 Validator**:`DataRequired` / `Length` / `NumberRange` 等,让框架自动校验
4. **message 必填**:用于返回给前端的错误信息,方便调试
5. **唯一性校验**:通过自定义 `validate_` 方法实现,使用 Repository 层的 `exists_by(filters, exclude_id)` 方法
## 总结
WTForms Schema 通过 `BaseForm` 继承公共字段(id 等),使用 `validators` 声明约束,自定义 `validate_` 方法实现唯一性校验。唯一性校验使用 `Repository.exists_by({'field': value}, exclude_id=current_id)` 方法,自动过滤软删除记录并支持排除自身。特殊操作单独建类,`message` 必填用于返回给前端的错误信息。