253 lines
6.6 KiB
Markdown
253 lines
6.6 KiB
Markdown
# Model层规范
|
||
|
||
## 概述
|
||
|
||
Model 层负责定义数据库表结构,所有业务模型继承 `BaseModel` 抽象基类,自动获得公共字段(id、create_user、create_time、update_user、update_time、is_delete)。
|
||
|
||
:::tip 设计原则
|
||
- 所有模型继承 `BaseModel`
|
||
- 字段命名使用 snake_case
|
||
- 每个字段必须有 `db_comment` 注释
|
||
- 使用 `get_table_name()` 生成表名
|
||
:::
|
||
|
||
## BaseModel 基类
|
||
|
||
`application/models.py` 定义了 `BaseModel` 抽象基类:
|
||
|
||
```python
|
||
from django.db import models
|
||
|
||
class BaseModel(models.Model):
|
||
"""基础模型抽象类"""
|
||
|
||
# 主键ID字段
|
||
id = models.AutoField(
|
||
auto_created=True,
|
||
primary_key=True,
|
||
serialize=False,
|
||
verbose_name='主键ID',
|
||
db_comment='主键ID'
|
||
)
|
||
|
||
# 创建人字段
|
||
create_user = models.CharField(
|
||
null=True, max_length=50, default=None,
|
||
verbose_name='创建人', db_comment='创建人'
|
||
)
|
||
|
||
# 创建时间字段
|
||
create_time = models.DateTimeField(
|
||
null=True, auto_now_add=True,
|
||
verbose_name="创建时间", db_comment='创建时间'
|
||
)
|
||
|
||
# 更新人字段
|
||
update_user = models.CharField(
|
||
null=True, max_length=50, default=None,
|
||
verbose_name='更新人', db_comment='更新人'
|
||
)
|
||
|
||
# 更新时间字段
|
||
update_time = models.DateTimeField(
|
||
null=True, auto_now=True,
|
||
verbose_name="更新时间", db_comment='更新时间'
|
||
)
|
||
|
||
# 逻辑删除标识
|
||
is_delete = models.BooleanField(
|
||
default=0, db_default=0,
|
||
verbose_name="逻辑删除",
|
||
db_comment='逻辑删除:0-正常 1-已删除'
|
||
)
|
||
|
||
class Meta:
|
||
abstract = True
|
||
```
|
||
|
||
## 完整示例
|
||
|
||
以案例模块为例,`application/example/models.py`:
|
||
|
||
```python
|
||
from django.db import models
|
||
from application.models import BaseModel
|
||
from utils.common import get_table_name
|
||
|
||
|
||
class Example(BaseModel):
|
||
"""案例模型类"""
|
||
|
||
# 案例名称
|
||
name = models.CharField(
|
||
null=False, max_length=100, db_index=True,
|
||
verbose_name="案例名称",
|
||
help_text="案例名称",
|
||
db_comment='案例名称'
|
||
)
|
||
|
||
# 案例图片
|
||
avatar = models.CharField(
|
||
null=True, blank=True, max_length=255,
|
||
verbose_name="案例图片",
|
||
help_text="案例图片",
|
||
db_comment='案例图片'
|
||
)
|
||
|
||
# 案例类型
|
||
TYPE_CHOICES = (
|
||
(1, "类型1"),
|
||
(2, "类型2"),
|
||
(3, "类型3"),
|
||
(4, "类型4"),
|
||
)
|
||
type = models.IntegerField(
|
||
null=False, choices=TYPE_CHOICES,
|
||
verbose_name="案例类型:1-类型1 2-类型2 3-类型3 4-类型4",
|
||
help_text="案例类型:1-类型1 2-类型2 3-类型3 4-类型4",
|
||
db_comment='案例类型:1-类型1 2-类型2 3-类型3 4-类型4'
|
||
)
|
||
|
||
# 案例状态
|
||
STATUS_CHOICES = (
|
||
(1, "正常"),
|
||
(2, "禁用"),
|
||
)
|
||
status = models.IntegerField(
|
||
null=False, choices=STATUS_CHOICES,
|
||
verbose_name="案例状态:1-正常 2-禁用",
|
||
help_text="案例状态:1-正常 2-禁用",
|
||
db_comment='案例状态:1-正常 2-禁用'
|
||
)
|
||
|
||
# 排序
|
||
sort = models.IntegerField(
|
||
null=False,
|
||
verbose_name="排序",
|
||
help_text="排序",
|
||
db_comment='排序'
|
||
)
|
||
|
||
class Meta:
|
||
db_table = get_table_name('example')
|
||
db_table_comment = "案例表"
|
||
verbose_name = "案例表"
|
||
verbose_name_plural = verbose_name
|
||
ordering = ("sort",)
|
||
|
||
def __str__(self):
|
||
return f"案例{self.name}"
|
||
```
|
||
|
||
## 字段定义规范
|
||
|
||
### 字段类型映射
|
||
|
||
| 数据库类型 | Django 字段类型 | 说明 |
|
||
|:----|:----|:----|
|
||
| varchar | CharField | 短文本,需指定 max_length |
|
||
| text | TextField | 长文本 |
|
||
| int | IntegerField | 整数 |
|
||
| datetime | DateTimeField | 日期时间 |
|
||
| date | DateField | 日期 |
|
||
| float | FloatField | 浮点数 |
|
||
| decimal | DecimalField | 精确小数 |
|
||
| boolean | BooleanField | 布尔值 |
|
||
|
||
### 字段属性
|
||
|
||
| 属性 | 说明 | 示例 |
|
||
|:----|:----|:----|
|
||
| `null` | 数据库是否允许 NULL | `null=True` |
|
||
| `blank` | 表单验证是否允许空 | `blank=True` |
|
||
| `max_length` | 最大长度 | `max_length=100` |
|
||
| `default` | 默认值 | `default=0` |
|
||
| `db_index` | 是否创建索引 | `db_index=True` |
|
||
| `choices` | 选项列表 | `choices=STATUS_CHOICES` |
|
||
| `verbose_name` | 字段中文名 | `verbose_name="案例名称"` |
|
||
| `help_text` | 帮助文本 | `help_text="案例名称"` |
|
||
| `db_comment` | 数据库字段注释 | `db_comment='案例名称'` |
|
||
|
||
### 选项字段定义
|
||
|
||
```python
|
||
# 状态字段
|
||
STATUS_CHOICES = (
|
||
(1, "正常"), # 正常状态
|
||
(2, "禁用"), # 禁用状态
|
||
)
|
||
status = models.IntegerField(
|
||
null=False, choices=STATUS_CHOICES,
|
||
verbose_name="状态:1-正常 2-禁用",
|
||
db_comment='状态:1-正常 2-禁用'
|
||
)
|
||
```
|
||
|
||
:::tip 选项定义约定
|
||
- 选项定义为类属性,命名为 `{FIELD}_CHOICES`
|
||
- 选项格式为 `(value, label)` 元组
|
||
- 字段注释中包含选项说明(如 `1-正常 2-禁用`)
|
||
:::
|
||
|
||
## Meta 类配置
|
||
|
||
```python
|
||
class Meta:
|
||
# 数据表名(使用 get_table_name 自动生成)
|
||
db_table = get_table_name('example')
|
||
|
||
# 表注释(Django 4.1+)
|
||
db_table_comment = "案例表"
|
||
|
||
# Admin 后台显示名称
|
||
verbose_name = "案例表"
|
||
verbose_name_plural = verbose_name
|
||
|
||
# 默认排序
|
||
ordering = ("sort",)
|
||
```
|
||
|
||
### get_table_name 函数
|
||
|
||
`utils/common.py` 中的 `get_table_name` 函数自动拼接表前缀:
|
||
|
||
```python
|
||
from config.env import DATABASE_PREFIX
|
||
|
||
def get_table_name(table_name):
|
||
"""获取带前缀的表名"""
|
||
return f"{DATABASE_PREFIX}{table_name}"
|
||
```
|
||
|
||
例如:`DATABASE_PREFIX = "django_"` 时,`get_table_name('example')` 返回 `"django_example"`。
|
||
|
||
## 索引定义
|
||
|
||
```python
|
||
class Example(BaseModel):
|
||
name = models.CharField(max_length=100, db_index=True) # 单列索引
|
||
|
||
class Meta:
|
||
indexes = [
|
||
models.Index(fields=['status', 'type'], name='idx_status_type'), # 复合索引
|
||
]
|
||
```
|
||
|
||
## 信号机制
|
||
|
||
项目使用 `class_prepared` 信号自动重排字段顺序,将公共字段(id、create_user 等)移到业务字段之后:
|
||
|
||
```python
|
||
from django.db.models.signals import class_prepared
|
||
|
||
def _reorder_fields(sender, **kwargs):
|
||
"""重排模型字段顺序,将公共字段移到最后"""
|
||
# ... 实现逻辑 ...
|
||
|
||
class_prepared.connect(_reorder_fields)
|
||
```
|
||
|
||
## 总结
|
||
|
||
Model 层通过继承 `BaseModel` 获得公共字段,使用 `get_table_name()` 生成带前缀的表名。字段定义需包含 `db_comment` 注释、`verbose_name` 中文名。选项字段使用 `choices` 参数定义,排序使用 `Meta.ordering` 配置。
|