8.2 KiB
8.2 KiB
:::tip 本章概要 代码生成器模板的 Jinja2 语法说明,包括模板变量、字段属性和自定义模板的开发方法。 :::
自定义模板
模板文件位于 src/modules/generator/templates/,使用 Jinja2 语法。修改模板后重新生成即可生效。
模板目录结构
templates/
├── __init__.py.tpl # 模块初始化
├── models.py.tpl # ORM 模型
├── schemas.py.tpl # Pydantic 表单
├── repository.py.tpl # 数据访问层
├── service.py.tpl # 业务逻辑层
├── endpoint.py.tpl # HTTP 端点
├── ui/ # 普通分页列表模板
│ ├── index.vue.tpl # 主页面
│ ├── edit.vue.tpl # 编辑弹窗
│ ├── detail.vue.tpl # 详情弹窗
│ ├── columns.ts.tpl # 表格列定义
│ ├── querySchemas.ts.tpl # 搜索表单 Schema
│ └── api.ts.tpl # API 请求封装
└── ui2/ # 树状列表模板
├── index.vue.tpl # 树形主页面
├── edit.vue.tpl # 树形编辑弹窗
└── detail.vue.tpl # 树形详情弹窗
ui/ vs ui2/ 切换逻辑
当表中存在 parent_id 或 pid 字段时,自动切换为 ui2/ 树形模板:
字段列表中是否有 parent_id / pid?
├── 是 → 使用 ui2/ 模板(3 个组件 + 1 个 API)
└── 否 → 使用 ui/ 模板(5 个组件 + 1 个 API)
两者共用 ui/api.ts.tpl 生成 API 接口文件。
模板变量
基础变量
| 变量 | 类型 | 说明 | 示例 |
|---|---|---|---|
app_name |
str | 模块名 | example |
module_comment |
str | 中文名 | 案例 |
module_comment_py |
str | 转义后中文名 | 案例 |
model_class_name |
str | 类名 | Example |
permission_prefix |
str | 权限前缀 | sys:example |
display_field |
str | 显示字段 | name |
sort_field |
str | 排序字段 | sort |
功能标识
| 变量 | 类型 | 说明 |
|---|---|---|
has_sort |
bool | 有排序字段 |
has_status |
bool | 有状态字段 |
has_status_route |
bool | 生成状态路由 |
has_image_field |
bool | 有图片字段 |
has_rich_text_field |
bool | 有富文本字段 |
serialize_maps |
dict | 枚举映射 {field_name: dict_code} |
字段列表
| 变量 | 类型 | 说明 |
|---|---|---|
fields |
list | 全部字段 |
form_fields |
list | 表单字段(in_form=True) |
filter_fields |
list | 精确匹配字段 |
like_fields |
list | 模糊查询字段 |
前端专用变量
| 变量 | 类型 | 说明 |
|---|---|---|
module_name |
str | 驼峰模块名 |
model_class_name_camel |
str | 驼峰类名(首字母小写) |
list_fields |
list | 列表展示字段 |
searchable_fields |
list | 可搜索字段 |
editable_fields |
list | 可编辑字段 |
is_tree_structure |
bool | 是否树形结构 |
parent_id_field |
str | 父级字段名 |
route_prefix |
str | 路由前缀 |
api_path |
str | API 路径 |
has_search_form |
bool | 显示搜索表单 |
show_selection |
bool | 显示复选框 |
action_column_width |
int | 操作列宽度 |
字段对象属性
fields 列表中每个字段对象包含以下属性:
基础属性
| 属性 | 类型 | 说明 |
|---|---|---|
name |
str | 字段名(snake_case) |
camel_name |
str | 驼峰字段名 |
comment |
str | 字段注释 |
py_comment |
str | 转义后注释(可嵌入 Python 字符串) |
label |
str | 清洗后的注释(去掉选项说明) |
类型属性
| 属性 | 类型 | 说明 |
|---|---|---|
db_type |
str | SQLAlchemy 类型 |
db_args |
str | 类型参数(如 "100") |
form_type |
str | Pydantic 类型 |
type_display |
str | 中文类型名 |
max_length |
int | 最大长度 |
约束属性
| 属性 | 类型 | 说明 |
|---|---|---|
nullable |
bool | 是否可空 |
required |
bool | 是否必填 |
is_unique |
bool | 是否唯一 |
default |
any | 默认值 |
default_repr |
str | 默认值 Python 表示 |
功能标识
| 属性 | 类型 | 说明 |
|---|---|---|
is_image |
bool | 图片字段 |
is_rich_text |
bool | 富文本字段 |
is_status |
bool | 状态字段 |
is_sort |
bool | 排序字段 |
展示控制
| 属性 | 类型 | 说明 |
|---|---|---|
in_form |
bool | 出现在表单中 |
in_list |
bool | 出现在列表中 |
editable |
bool | 可编辑 |
filterable |
bool | 作为查询条件 |
filter_type |
str | 筛选类型 |
searchable |
bool | 可搜索 |
选项属性
| 属性 | 类型 | 说明 |
|---|---|---|
choices |
list | [(value, label), ...] |
dict_code |
str | 数据字典编码 |
min_value |
int | 最小值 |
max_value |
int | 最大值 |
前端专用
| 属性 | 类型 | 说明 |
|---|---|---|
purpose |
str | 用途说明 |
type |
str | 配置字段类型名(CharField/IntegerField/...) |
choices[].value |
any | 选项值 |
choices[].label |
str | 选项文本 |
choices[].type |
str | 标签颜色(success/danger/warning/info) |
自定义模板示例
示例 1:为所有字段添加索引
修改 templates/models.py.tpl:
{% if f.db_type == 'String' %}
{{ f.name }} = Column(String({{ f.db_args or 255 }}), nullable={{ 'True' if f.nullable else 'False' }}, index=True, comment="{{ f.comment }}")
{% endif %}
示例 2:自定义 Service 钩子
修改 templates/service.py.tpl,在 Service 类中添加钩子方法:
class {{ model_class_name }}Service(BaseService({{ model_class_name }}]):
# ... 差异点声明 ...
{% if has_status %}
def _before_delete(self, ids) -> Optional[str]:
"""删除前校验:检查是否有关联数据"""
# TODO: 添加自定义校验逻辑
return None
{% endif %}
示例 3:自定义前端表格列宽
修改 templates/ui/columns.ts.tpl:
{% for f in list_fields %}
{
label: '{{ f.label }}',
prop: '{{ f.camel_name }}',
minWidth: {{ 80 + (f.max_length or 100) // 3 }}, {# 动态列宽 #}
},
{% endfor %}
新增模板
如需生成额外文件(如测试文件),按以下步骤操作:
第 1 步:创建模板文件
在 templates/ 下新建 test.py.tpl:
# tests/test_{{ app_name }}.py
import pytest
from modules.{{ app_name }}.service import {{ app_name }}_service
class Test{{ model_class_name }}:
def test_get_page(self, client):
response = client.get("/api/v1/{{ app_name }}/page")
assert response.status_code == 200
第 2 步:注册到 BACKEND_TEMPLATES
在 code_generator.py 中添加:
BACKEND_TEMPLATES = [
'__init__.py.tpl',
'models.py.tpl',
'schemas.py.tpl',
'repository.py.tpl',
'service.py.tpl',
'test.py.tpl', # 新增
]
第 3 步:调整输出路径(可选)
如果需要输出到非默认目录,修改 generate() 方法中的路径逻辑。
Jinja2 语法速查
{# 注释 #}
{# 变量输出 #}
{{ app_name }}
{{ f.name | upper }}
{# 条件判断 #}
{% if has_status %}
# 有状态字段
{% elif has_sort %}
# 有排序字段
{% else %}
# 都没有
{% endif %}
{# 循环 #}
{% for f in fields %}
{{ f.name }}
{% endfor %}
{# 循环 + 条件过滤 #}
{% for f in fields if f.is_image %}
{{ f.name }}
{% endfor %}
{# 循环变量 #}
{% for f in fields %}
{{ loop.index }} {# 从 1 开始 #}
{{ loop.first }} {# 是否第一个 #}
{{ loop.last }} {# 是否最后一个 #}
{% endfor %}
{# 三元表达式 #}
{{ 'True' if f.nullable else 'False' }}
{# 字符串拼接 #}
{{ app_name }}_repo
{# 默认值 #}
{{ f.db_args or 255 }}
总结
模板使用 Jinja2 语法,每个模板对应一个生成文件。模板变量包括模块名、表名、字段列表等,字段属性包含名称、类型、注释、是否可搜索等。可通过修改模板自定义生成代码的风格和结构。