227 lines
6.2 KiB
Markdown
227 lines
6.2 KiB
Markdown
# Form层规范
|
||
|
||
## 概述
|
||
|
||
Form 层负责数据验证,使用 Django `ModelForm` 实现。每个模块的 `forms.py` 定义表单验证类,包含字段验证规则和中文错误提示。
|
||
|
||
:::tip 设计原则
|
||
- 继承 `forms.ModelForm`
|
||
- 每个字段定义中文 `error_messages`
|
||
- 使用 `Meta.fields` 指定验证字段
|
||
- 验证在 Service 层 ORM 操作之前执行
|
||
:::
|
||
|
||
## 完整示例
|
||
|
||
以案例模块为例,`application/example/forms.py`:
|
||
|
||
```python
|
||
from django import forms
|
||
from application.example import models
|
||
|
||
|
||
class ExampleForm(forms.ModelForm):
|
||
"""案例表单验证类"""
|
||
|
||
# 案例名称
|
||
name = forms.CharField(
|
||
required=True,
|
||
max_length=100,
|
||
error_messages={
|
||
'required': '案例名称不能为空',
|
||
'max_length': '案例名称长度不得超过100个字符',
|
||
}
|
||
)
|
||
|
||
# 案例图片
|
||
avatar = forms.CharField(
|
||
required=False,
|
||
max_length=255,
|
||
error_messages={
|
||
'max_length': '案例图片长度不得超过255个字符',
|
||
}
|
||
)
|
||
|
||
# 案例类型
|
||
type = forms.IntegerField(
|
||
required=True,
|
||
min_value=1,
|
||
max_value=4,
|
||
error_messages={
|
||
'required': '案例类型不能为空',
|
||
'min_value': '案例类型值不能小于1',
|
||
'max_value': '案例类型值不能大于4',
|
||
}
|
||
)
|
||
|
||
# 案例状态
|
||
status = forms.IntegerField(
|
||
required=True,
|
||
min_value=1,
|
||
max_value=2,
|
||
error_messages={
|
||
'required': '案例状态不能为空',
|
||
'min_value': '案例状态值不能小于1',
|
||
'max_value': '案例状态值不能大于2',
|
||
}
|
||
)
|
||
|
||
# 排序
|
||
sort = forms.IntegerField(
|
||
required=True,
|
||
error_messages={
|
||
'required': '排序不能为空',
|
||
}
|
||
)
|
||
|
||
class Meta:
|
||
model = models.Example
|
||
fields = ['name', 'avatar', 'type', 'status', 'sort']
|
||
```
|
||
|
||
## 字段类型
|
||
|
||
| Django 表单字段 | 说明 | 常用参数 |
|
||
|:----|:----|:----|
|
||
| `CharField` | 字符串 | `max_length`, `min_length` |
|
||
| `IntegerField` | 整数 | `min_value`, `max_value` |
|
||
| `FloatField` | 浮点数 | `min_value`, `max_value` |
|
||
| `DecimalField` | 精确小数 | `max_digits`, `decimal_places` |
|
||
| `BooleanField` | 布尔值 | - |
|
||
| `DateTimeField` | 日期时间 | `input_formats` |
|
||
| `DateField` | 日期 | `input_formats` |
|
||
| `EmailField` | 邮箱 | `max_length` |
|
||
| `URLField` | URL | `max_length` |
|
||
|
||
## 错误消息规范
|
||
|
||
每个字段必须定义中文 `error_messages`:
|
||
|
||
```python
|
||
name = forms.CharField(
|
||
required=True,
|
||
max_length=100,
|
||
error_messages={
|
||
'required': '案例名称不能为空', # 必填验证
|
||
'max_length': '案例名称长度不得超过100个字符', # 长度验证
|
||
'min_length': '案例名称长度不能少于2个字符', # 最小长度
|
||
}
|
||
)
|
||
|
||
type = forms.IntegerField(
|
||
required=True,
|
||
min_value=1,
|
||
max_value=4,
|
||
error_messages={
|
||
'required': '案例类型不能为空', # 必填验证
|
||
'min_value': '案例类型值不能小于1', # 最小值验证
|
||
'max_value': '案例类型值不能大于4', # 最大值验证
|
||
'invalid': '案例类型格式不正确', # 格式验证
|
||
}
|
||
)
|
||
```
|
||
|
||
### 常用错误消息键
|
||
|
||
| 键 | 说明 | 适用字段 |
|
||
|:----|:----|:----|
|
||
| `required` | 必填验证 | 所有字段 |
|
||
| `max_length` | 最大长度 | CharField |
|
||
| `min_length` | 最小长度 | CharField |
|
||
| `max_value` | 最大值 | IntegerField/FloatField |
|
||
| `min_value` | 最小值 | IntegerField/FloatField |
|
||
| `invalid` | 格式验证 | 所有字段 |
|
||
| `unique` | 唯一性验证 | 需手动实现 |
|
||
|
||
## Meta 类配置
|
||
|
||
```python
|
||
class Meta:
|
||
# 绑定模型
|
||
model = models.Example
|
||
|
||
# 指定验证字段(只验证以下字段)
|
||
fields = ['name', 'avatar', 'type', 'status', 'sort']
|
||
|
||
# 排除字段(不验证以下字段)
|
||
# exclude = ['create_user', 'update_user']
|
||
```
|
||
|
||
:::tip fields vs exclude
|
||
- `fields`:白名单,只验证列出的字段
|
||
- `exclude`:黑名单,排除列出的字段
|
||
- 推荐使用 `fields`,更明确
|
||
:::
|
||
|
||
## 在 Service 中使用
|
||
|
||
```python
|
||
from application.example import forms
|
||
from utils import R, regular
|
||
|
||
def add_example(request):
|
||
"""添加案例"""
|
||
# 解析请求参数
|
||
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
|
||
# 使用 cleaned_data 创建记录
|
||
Example.objects.create(
|
||
name=cleaned_data.get('name'),
|
||
type=cleaned_data.get('type'),
|
||
status=cleaned_data.get('status'),
|
||
sort=cleaned_data.get('sort'),
|
||
)
|
||
return R.ok(msg="创建成功")
|
||
except Exception as e:
|
||
return R.failed(msg="添加失败")
|
||
```
|
||
|
||
## 错误信息提取
|
||
|
||
使用 `regular.get_err(form)` 提取表单验证错误信息:
|
||
|
||
```python
|
||
from utils import regular
|
||
|
||
form = forms.ExampleForm(data)
|
||
if not form.is_valid():
|
||
error_msg = regular.get_err(form) # 返回第一个错误的中文提示
|
||
return R.failed(msg=error_msg)
|
||
```
|
||
|
||
## 自定义验证方法
|
||
|
||
```python
|
||
class ExampleForm(forms.ModelForm):
|
||
name = forms.CharField(required=True, max_length=100)
|
||
|
||
def clean_name(self):
|
||
"""自定义验证:检查名称是否包含敏感词"""
|
||
name = self.cleaned_data.get('name')
|
||
if '敏感词' in name:
|
||
raise forms.ValidationError('名称包含敏感词')
|
||
return name
|
||
|
||
def clean(self):
|
||
"""全局验证:检查字段间的关系"""
|
||
cleaned_data = super().clean()
|
||
start_date = cleaned_data.get('start_date')
|
||
end_date = cleaned_data.get('end_date')
|
||
if start_date and end_date and start_date > end_date:
|
||
raise forms.ValidationError('开始日期不能晚于结束日期')
|
||
return cleaned_data
|
||
```
|
||
|
||
## 总结
|
||
|
||
Form 层使用 Django `ModelForm` 实现数据验证,每个字段定义中文 `error_messages`。在 Service 层 ORM 操作之前执行验证,使用 `regular.get_err(form)` 提取错误信息。支持自定义验证方法(`clean_{field}`)和全局验证(`clean`)。
|