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

441 lines
13 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.
# 业务逻辑层(Service)
业务逻辑层是模块开发的第三步,也是最核心的部分。所有 CRUD 操作、业务规则、数据处理都封装在 Service 层的独立函数中。
## 文件位置
```
application/example/services.py
```
## 命名规范
| 函数类型 | 命名格式 | 示例 |
|:----|:----|:----|
| 分页查询 | `get_<m>_page` | `get_example_page` |
| 列表查询 | `get_<m>_list` | `get_example_list` |
| 详情查询 | `get_<m>_detail` | `get_example_detail` |
| 添加 | `add_<m>` | `add_example` |
| 更新 | `update_<m>` | `update_example` |
| 删除 | `delete_<m>s` | `delete_examples` |
| 状态更新 | `update_<m>_status` | `update_example_status` |
| 数据列表 | `get_<m>_data` | `get_example_data` |
## 分页查询
```python
def get_example_page(request):
"""
查询案例分页数据
Args:
request: HttpRequest对象
- pageNo: 当前页码(可选,默认为1)
- pageSize: 每页记录数(可选,默认为PAGE_SIZE)
- name: 案例名称(可选,模糊查询)
- type: 案例类型(可选,精确匹配)
- status: 案例状态(可选,精确匹配)
Returns:
R: 包含分页数据的JSON响应
"""
try:
# 获取分页参数
page_no = int(request.GET.get('pageNo', 1))
page_size = int(request.GET.get('pageSize', PAGE_SIZE))
# 构建基础查询(只查询未删除的记录)
query = Example.objects.filter(is_delete=False)
# 应用筛选条件
query = _apply_filters(query, request)
# 按排序字段升序排列
query = query.order_by("sort")
# 执行分页查询
paginator = Paginator(query, page_size)
try:
page_list = paginator.page(page_no)
except (PageNotAnInteger, InvalidPage, EmptyPage):
page_list = paginator.page(1)
page_no = 1
# 构建并返回分页数据
page_data = _build_page_data(page_list, paginator, page_no, page_size)
return R.ok(data=page_data)
except Exception as e:
logging.error(f"查询案例分页数据异常: {str(e)}")
return R.failed(msg="查询失败,请稍后重试")
```
### 筛选条件应用
```python
def _apply_filters(queryset, request):
"""应用查询筛选条件(私有辅助函数)"""
# 案例名称模糊筛选
name = request.GET.get('name')
if name:
queryset = queryset.filter(name__contains=name)
# 案例类型筛选(精确匹配)
type = request.GET.get('type')
if type:
queryset = queryset.filter(type=type)
# 案例状态筛选(精确匹配)
status = request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
return queryset
```
### 分页数据构建
```python
def _build_page_data(page_list, paginator, page_no, page_size):
"""构建分页返回数据(私有辅助函数)"""
records = []
for item in page_list:
record = {
'id': item.id,
'name': item.name,
'avatar': get_file_url(item.avatar) if item.avatar else "",
'type': item.type,
'status': item.status,
'sort': item.sort,
'createUser': item.create_user,
'createTime': format_datetime(item.create_time),
'updateUser': item.update_user,
'updateTime': format_datetime(item.update_time),
}
records.append(record)
return {
'records': records,
'total': paginator.count,
'size': page_size,
'current': page_no,
'pages': paginator.num_pages
}
```
### 分页响应格式
```json
{
"code": 0,
"data": {
"records": [...],
"total": 100,
"size": 10,
"current": 1,
"pages": 10
},
"msg": "操作成功",
"ok": true
}
```
## 详情查询
```python
def get_example_detail(id):
"""
根据ID查询案例详情
Args:
id: 案例ID
Returns:
dict/None: 案例详情字典,不存在时返回None
"""
try:
instance = Example.objects.filter(is_delete=False, id=id).first()
if not instance:
return None
return {
'id': instance.id,
'name': instance.name,
'avatar': get_file_url(instance.avatar) if instance.avatar else "",
'type': instance.type,
'status': instance.status,
'sort': instance.sort,
}
except Exception as e:
logging.error(f"查询案例详情异常,ID: {id}, 错误: {str(e)}")
return None
```
## 添加记录
```python
def add_example(request):
"""
添加案例
处理流程:
1. 解析请求体中的JSON数据
2. 表单验证
3. 处理头像图片(保存到 example 目录)
4. 创建数据库记录
"""
# 解析请求参数
data, error = parse_request_body(request)
if error:
return R.failed(msg=error)
# 表单验证
form = forms.ExampleForm(data)
if not form.is_valid():
return R.failed(msg=regular.get_err(form))
try:
cleaned_data = form.cleaned_data
# 处理头像图片
avatar = cleaned_data.get('avatar')
if avatar:
avatar = save_file(avatar, "example")
# 创建案例记录
Example.objects.create(
name=cleaned_data.get('name'),
avatar=avatar,
type=cleaned_data.get('type'),
status=cleaned_data.get('status'),
sort=cleaned_data.get('sort'),
create_user=get_username(request),
update_user=get_username(request)
)
return R.ok(msg="创建成功")
except Exception as e:
logging.error(f"添加案例异常: {str(e)}")
return R.failed(msg="添加失败,请稍后重试")
```
### 添加流程说明
```
请求 → parse_request_body → 表单验证 → 处理文件 → ORM.create → 返回结果
```
| 步骤 | 函数 | 说明 |
|:----|:----|:----|
| 解析请求体 | `parse_request_body(request)` | 解析 JSON 请求体 |
| 表单验证 | `forms.ExampleForm(data)` | 验证数据合法性 |
| 处理文件 | `save_file(url, dir)` | 临时文件迁移到正式目录 |
| 创建记录 | `Example.objects.create(...)` | ORM 创建数据库记录 |
| 获取用户名 | `get_username(request)` | 从 JWT token 解析当前用户 |
## 更新记录
```python
def update_example(request):
"""
更新案例
处理流程:
1. 解析请求体中的JSON数据
2. 验证案例ID
3. 表单验证
4. 查询现有记录
5. 处理头像图片(如有变化则保存新图片)
6. 更新各字段
7. 保存更新
"""
# 解析请求参数
data, error = parse_request_body(request)
if error:
return R.failed(msg=error)
# 验证案例ID
instance_id = data.get('id')
if not instance_id or not str(instance_id).isdigit() or int(instance_id) <= 0:
return R.failed("案例ID无效")
instance_id = int(instance_id)
# 表单验证
form = forms.ExampleForm(data)
if not form.is_valid():
return R.failed(msg=regular.get_err(form))
try:
# 查询案例记录
instance = Example.objects.filter(id=instance_id, is_delete=False).first()
if not instance:
return R.failed("案例不存在")
cleaned_data = form.cleaned_data
# 处理头像图片(仅当有新图片且与当前不同时保存)
avatar = cleaned_data.get('avatar')
if avatar and avatar != instance.avatar:
avatar = save_file(avatar, "example")
else:
avatar = instance.avatar
# 更新各字段
instance.name = cleaned_data.get('name')
instance.avatar = avatar
instance.type = cleaned_data.get('type')
instance.status = cleaned_data.get('status')
instance.sort = cleaned_data.get('sort')
instance.update_user = get_username(request)
instance.save()
return R.ok(msg="更新成功")
except Exception as e:
logging.error(f"更新案例异常,ID: {instance_id}, 错误: {str(e)}")
return R.failed(msg="更新失败,请稍后重试")
```
## 删除记录(批量)
```python
def delete_examples(instance_ids):
"""
删除案例(批量删除)
采用逻辑删除方式(is_delete=True),而非物理删除。
支持逗号分隔的多个ID。
Args:
instance_ids: 案例ID字符串,多个ID用逗号分隔
例如: "1" 或 "1,2,3"
"""
if not instance_ids:
return R.failed("记录ID不存在")
try:
# 解析ID列表
id_list = [int(id_str.strip()) for id_str in instance_ids.split(',') if id_str.strip()]
if not id_list:
return R.failed("无效的记录ID")
# 批量更新删除标识(逻辑删除)
updated_count = Example.objects.filter(
id__in=id_list,
is_delete=False
).update(is_delete=True)
return R.ok(msg=f"本次共删除{updated_count}条数据")
except ValueError:
return R.failed("记录ID格式错误")
except Exception as e:
logging.error(f"删除案例异常,IDs: {instance_ids}, 错误: {str(e)}")
return R.failed(msg="删除失败,请稍后重试")
```
### 逻辑删除说明
项目采用逻辑删除(软删除),而非物理删除:
```python
# 逻辑删除:设置 is_delete=True
Example.objects.filter(id__in=id_list).update(is_delete=True)
# 物理删除(不推荐):真正删除数据库记录
Example.objects.filter(id__in=id_list).delete()
```
所有查询都必须加 `is_delete=False` 条件过滤已删除的数据。
## 状态更新
```python
def update_example_status(request):
"""
更新案例状态
单独更新案例的启用/禁用状态,不涉及其他字段。
"""
# 解析请求参数
data, error = parse_request_body(request)
if error:
return R.failed(msg=error)
# 验证案例ID
instance_id = data.get('id')
if not instance_id or not str(instance_id).isdigit() or int(instance_id) <= 0:
return R.failed("案例ID无效")
instance_id = int(instance_id)
# 验证状态值(1=启用, 2=禁用)
status = data.get('status')
if status is None:
return R.failed("状态值不能为空")
try:
status = int(status)
if status not in [1, 2]:
return R.failed("状态值无效")
except ValueError:
return R.failed("状态值格式错误")
try:
instance = Example.objects.filter(id=instance_id, is_delete=False).first()
if not instance:
return R.failed("案例不存在")
instance.status = status
instance.update_user = get_username(request)
instance.save()
return R.ok(msg="状态更新成功")
except Exception as e:
logging.error(f"更新案例状态异常,ID: {instance_id}, 错误: {str(e)}")
return R.failed(msg="状态更新失败,请稍后重试")
```
## 数据列表(下拉选择)
```python
def get_example_data():
"""
获取所有启用的案例列表
通常用于前端下拉选择框等场景。
只返回 status=1(启用)且未删除的记录。
"""
try:
queryset = Example.objects.filter(
is_delete=False,
status=1
).order_by("sort").values()
return list(queryset)
except Exception as e:
logging.error(f"获取案例列表异常: {str(e)}")
return []
```
## 常用工具函数
| 函数 | 来源 | 用途 |
|:----|:----|:----|
| `parse_request_body(request)` | `utils/common.py` | 解析 JSON 请求体 |
| `regular.get_err(form)` | `utils/regular.py` | 提取表单错误信息 |
| `get_username(request)` | `utils/security.py` | 从 JWT 获取当前用户名 |
| `get_file_url(path)` | `utils/common.py` | 拼接文件完整 URL |
| `save_file(url, dir)` | `utils/common.py` | 保存文件到正式目录 |
| `format_datetime(dt)` | `utils/common.py` | 格式化日期时间 |
| `R.ok()` / `R.failed()` | `utils/R.py` | 统一响应格式 |
## 开发要点
1. **所有查询都要加 `is_delete=False`**:过滤已删除的数据
2. **使用 `parse_request_body()` 解析请求体**:统一的 JSON 解析方式
3. **表单验证失败时使用 `regular.get_err()` 提取错误**:统一的错误信息格式
4. **使用 `get_username(request)` 获取当前用户**:从 JWT token 解析
5. **文件处理使用 `save_file()`**:临时文件迁移到正式目录
6. **批量删除支持逗号分隔的多个 ID**:如 "1,2,3"
7. **所有异常都要捕获并记录日志**:使用 `logging.error()`
## 总结
Service 层封装所有业务逻辑,每个 CRUD 操作都是独立的函数。使用 `parse_request_body` 解析请求体,`forms.ModelForm` 验证数据,`R.ok()` / `R.failed()` 返回统一响应。所有查询都必须加 `is_delete=False` 过滤已删除数据,批量删除采用逻辑删除方式。