优化更新

This commit is contained in:
2026-09-13 21:53:16 +08:00
parent 8548fe9ea1
commit 3f8626fb10
84 changed files with 2058 additions and 2594 deletions
+142 -105
View File
@@ -10,115 +10,173 @@ src/modules/system/position/schema.py
## 完整代码
以下为 `src/modules/system/position/schema.py` 的核心代码(省略版权头):
```python
from wtforms import StringField, IntegerField
"""
岗位表单验证模块
=============================================================
提供岗位相关的表单验证功能,包括:
- 岗位添加/更新表单验证
- 岗位状态设置表单验证
"""
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):
"""岗位创建/编辑表单"""
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之间')
])
"""
岗位表单验证类
用于添加和更新岗位时的数据验证。
添加时 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('岗位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 = 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` | 创建/编辑通用表单 | 自带可选 `id` 字段(`Optional`),编辑时传入 |
| 特定操作表单 | 无额外字段,按需定义 |
| `BaseForm` | 创建/编辑通用表单 | 继承 `flask_wtf.FlaskForm`,支持 ArrayField 处理 |
| `PositionStatusForm` | 状态更新专用 | 只包含 `id` 和 `status` 两个字段 |
创建和编辑共用 `PositionForm`:创建时不传 `id`,编辑时传入 `id`。`BaseForm` 的 `id` 字段定义为 `IntegerField('ID', validators=[Optional()])`,创建时可省略。
### id 字段
创建和编辑共用 `PositionForm`:创建时不传 `id`,编辑时传入 `id`。`id` 字段定义为 `Optional()`,创建时可省略:
```python
id = IntegerField(
label='岗位ID',
validators=[Optional(), NumberRange(min=1, message='岗位ID必须大于0')],
description='岗位唯一标识,更新时必填,添加时自动生成'
)
```
### Validator 约束
```python
name = StringField('岗位名称', validators=[
DataRequired(message='岗位名称不能为空'),
Length(min=1, max=150, message='岗位名称长度必须在1-150个字符之间')
])
```
| Validator | 含义 | 示例 |
|:----|:----|:----|
| `DataRequired` | 必填字段,不允许空值 | `DataRequired(message='岗位名称不能为空')` |
| `Length(max=150)` | 字符串长度限制 | `Length(max=150, message='...')` |
| `NumberRange(min=1, max=2)` | 数值范围限制 | `NumberRange(min=1, max=2, message='...')` |
| `Optional` | 可选字段 | `Optional()` |
| Validator | 含义 |
|:----|:----|
| `DataRequired` | 必填字段,不允许空值 |
| `Length(min=1, max=150)` | 字符串长度限制 |
| `NumberRange(min=1, max=2)` | 数值范围限制 |
| `Optional` | 可选字段 |
### 自定义校验器
通过定义 `validate_{field_name}` 方法实现自定义校验:
```python
status = IntegerField('岗位状态', validators=[
DataRequired(message='岗位状态不能为空'),
NumberRange(min=1, max=2, message='岗位状态必须是1或2')
])
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("岗位名称已存在")
```
| 参数 | 含义 |
|:----|:----|
| `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` 两个字段,与创建/编辑表单分开定义,接口语义更清晰。
- 方法名必须为 `validate_{field_name}`
- 校验失败抛出 `ValidationError(错误消息)`
- 唯一性校验通过 `Repository.exists_by(filters, exclude_id)` 实现,自动过滤软删除记录
## 如何在 Router 中使用
@@ -141,7 +199,7 @@ def add():
return R.ok(msg="添加成功")
```
`parse_request_body` 函数自动解析请求体 JSON。校验失败时,Service 层抛出 `BizError`,由异常处理器自动返回标准错误响应:
校验失败时,由框架自动返回标准错误响应:
```json
{
@@ -151,36 +209,15 @@ def add():
}
```
## 自定义校验器
如果需要自定义校验逻辑,可以在表单类中定义 `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. **特殊操作单独建类**:如状态更新、排序调整等,只包含必要的字段
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` 继承公共字段(id 等),使用 `validators` 声明约束,自定义 `validate_` 方法实现唯一性校验。唯一性校验使用 `Repository.exists_by({'field': value}, exclude_id=current_id)` 方法,自动过滤软删除记录并支持排除自身。特殊操作单独建类,`message` 必填用于返回给前端的错误信息。
WTForms Schema 通过 `BaseForm` 继承公共能力,使用 `validators` 声明约束,自定义 `validate_` 方法实现唯一性校验。唯一性校验使用 `Repository.exists_by({'field': value}, exclude_id=current_id)` 方法,自动过滤软删除记录并支持排除自身。特殊操作单独建类,`message` 必填用于返回给前端的错误信息。