Files
djangoadmin-wiki/DjangoAdmin_Flask_AntdVue/zh/volume4/business/example.md
T
2026-09-12 23:12:15 +08:00

192 lines
7.0 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.
:::tip 案例演示
案例演示模块是代码生成器的参考模板,展示了标准 CRUD 模块的完整实现。包含文件字段处理、枚举字典映射、状态管理等功能,可作为新模块开发的参考范例。
:::
## 模块结构
```
src/modules/example/
├── models.py # 案例模型
├── schema.py # 表单验证(WTForms)
├── repository.py # 数据访问层
├── service.py # 业务逻辑层(继承 BaseService)
└── router.py # 路由控制器
```
## 案例模型
```python
# src/modules/example/models.py
class Example(BaseModel, BaseDB):
"""
案例演示模型
包含文件字段(avatar)、枚举字段(type/status)等典型业务字段。
"""
__tablename__ = DB_PREFIX + "example"
__table_comment__ = "案例演示表"
# 案例名称
name = db.Column(db.String(255), nullable=False, index=True, comment="案例名称")
# 案例图片(存相对路径)
avatar = db.Column(db.String(255), nullable=False, comment="案例图片")
# 案例类型:1-类型1 2-类型2 3-类型3 4-类型4
type = db.Column(db.Integer, nullable=True, default=0, server_default=text('0'),
comment="案例类型:1-类型1 2-类型2 3-类型3 4-类型4")
# 案例状态:1-在用 2-停用
status = db.Column(db.Integer, nullable=True, default=0, server_default=text('0'),
index=True, comment="案例状态:1-在用 2-停用")
# 排序
sort = db.Column(db.Integer, nullable=True, default=0, server_default=text('0'),
comment="案例排序")
```
## 服务层实现
```python
# src/modules/example/service.py
class ExampleService(BaseService):
"""
案例演示服务类
展示文件字段处理、枚举映射等标准 CRUD 模式。
"""
repository = ExampleRepository
form = ExampleForm
status_form = ExampleStatusForm
model_name = "案例演示"
@classmethod
def before_add(cls, form):
"""新增前钩子:处理文件字段"""
_handle_file_fields(form)
@classmethod
def before_update(cls, form, obj):
"""更新前钩子:处理文件字段"""
_handle_file_fields(form)
@classmethod
def _enrich_page_records(cls, records):
"""分页富化:文件字段拼接 URL"""
for data in records:
data["avatar"] = get_file_url(data.get("avatar"))
return records
@classmethod
def _detail_dict(cls, obj):
"""详情序列化:文件字段拼接 URL"""
data = model_to_dict(obj)
data["avatar"] = get_file_url(data.get("avatar"))
return data
def _handle_file_fields(form):
"""文件字段处理:临时文件迁移到正式目录"""
avatar = form.data.get("avatar")
if avatar:
form.avatar.data = save_file(avatar, "example")
```
### 文件字段处理模式
这是项目中处理文件字段的标准模式:
| 步骤 | 时机 | 处理 |
|:---|:---|:---|
| 1. 前端上传 | 用户选择文件 | 上传到临时目录,获得临时 URL |
| 2. 提交表单 | 前端提交 | 将临时 URL 传给后端 |
| 3. `before_add`/`before_update` | Service 钩子 | `save_file` 迁移到正式目录 |
| 4. 存储数据库 | Repository.create | 存相对路径 |
| 5. 查询返回 | `_detail_dict`/`_enrich_page_records` | `get_file_url` 拼接完整 URL |
### 枚举字段映射
通过 `serialize_maps` 声明枚举字段与字典编码的映射:
```python
class ExampleService(BaseService):
serialize_maps = {
'type': 'example_type', # 案例类型 → 数据字典
'status': 'example_status' # 案例状态 → 数据字典
}
```
查询结果自动包含 `typeText` 和 `statusText` 字段。
## 表单验证
```python
# src/modules/example/schema.py
class ExampleForm(BaseForm):
"""案例创建/编辑表单"""
name = StringField('案例名称', validators=[
DataRequired(message='案例名称不能为空'),
Length(max=255, message='案例名称长度不得超过255个字符')
])
avatar = StringField('案例图片', validators=[Optional(), Length(max=255)])
type = IntegerField('案例类型', validators=[
Optional(), NumberRange(min=1, max=4, message='案例类型值在1~4之间')
])
status = IntegerField('案例状态', validators=[
Optional(), NumberRange(min=1, max=2, message='案例状态值在1~2之间')
])
sort = IntegerField('案例排序', validators=[
Optional(), NumberRange(min=0, max=99999)
])
def validate_name(self, field):
"""案例名称唯一性校验"""
if not field.data:
return
current_id = self.id.data if self.id.data else None
if ExampleRepository.exists_by({'name': field.data}, exclude_id=current_id):
raise ValidationError("案例名称已存在")
class ExampleStatusForm(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之间')
])
```
## API 接口
| 方法 | 路径 | 权限节点 | 说明 |
|:---|:---|:---|:---|
| GET | `/api/v1/example/page` | `sys:example:page` | 分页查询 |
| GET | `/api/v1/example/detail/<id>` | `sys:example:detail` | 详情查询 |
| GET | `/api/v1/example/list` | `sys:example:list` | 列表查询(无分页) |
| POST | `/api/v1/example/add` | `sys:example:add` | 新增案例 |
| PUT | `/api/v1/example/update` | `sys:example:update` | 编辑案例 |
| DELETE | `/api/v1/example/delete/<id>` | `sys:example:delete` | 删除案例 |
| DELETE | `/api/v1/example/batchDelete` | `sys:example:batchDelete` | 批量删除 |
| PUT | `/api/v1/example/status` | `sys:example:status` | 设置状态 |
## 数据字典
| 字典编码 | 用途 | 字典项示例 |
|:---|:---|:---|
| `example_type` | 案例类型名称 | 1=类型1, 2=类型2, 3=类型3, 4=类型4 |
| `example_status` | 案例状态名称 | 1=在用, 2=停用 |
## 作为代码生成器参考
案例演示模块是代码生成器的参考模板,新模块开发时可参考其模式:
1. **模型定义**:继承 `BaseModel + BaseDB`,字段注释完整
2. **表单验证**:继承 `BaseForm`,`validate_` 方法实现唯一性校验
3. **数据访问**:继承 `BaseRepository`,实现 `page()` / `list_enabled()`
4. **业务服务**:继承 `BaseService`,声明 `repository` / `form` / `model_name`
5. **路由控制**:Blueprint + `@check_permission` + `@check_demo` + `@operation_log`
## 总结
案例演示模块是标准 CRUD 模块的参考范例,展示了文件字段处理(`save_file`/`get_file_url`)、枚举字典映射(`serialize_maps`)、状态管理(`update_status`)等典型模式。新模块开发或代码生成器生成的代码均以此模块为参考。