:::tip 本章概要 代码生成器模板的 Jinja2 语法说明,包括模板变量、字段属性和自定义模板的开发方法。 ::: # 自定义模板 模板文件位于 `src/modules/generator/templates/`,使用 Jinja2 语法。修改模板后重新生成即可生效。 ## 模板目录结构 ``` templates/ ├── __init__.py.tpl # 模块初始化 ├── models.py.tpl # ORM 模型 ├── schema.py.tpl # WTForms 表单 ├── repository.py.tpl # 数据访问层 ├── service.py.tpl # 业务逻辑层 ├── router.py.tpl # 路由定义层 ├── 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 | WTForms 类型 | | `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`: ```jinja2 {% 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 类中添加钩子方法: ```jinja2 class {{ model_class_name }}Service(BaseService): repository = {{ model_class_name }}Repository form = {{ model_class_name }}Form status_form = {{ model_class_name }}StatusForm model_name = "{{ model_comment }}" {% if has_status %} @classmethod def before_delete(cls, ids): """删除前校验:检查是否有关联数据""" # TODO: 添加自定义校验逻辑 pass {% endif %} ``` ### 示例 3:自定义前端表格列宽 修改 `templates/ui/columns.ts.tpl`: ```jinja2 {% 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`: ```jinja2 # tests/test_{{ app_name }}.py import pytest from src.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` 中添加: ```python BACKEND_TEMPLATES = [ '__init__.py.tpl', 'models.py.tpl', 'schema.py.tpl', 'repository.py.tpl', 'service.py.tpl', 'router.py.tpl', 'test.py.tpl', # 新增 ] ``` ### 第 3 步:调整输出路径(可选) 如果需要输出到非默认目录,修改 `generate()` 方法中的路径逻辑。 ## Jinja2 语法速查 ```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 语法,每个模板对应一个生成文件。模板变量包括模块名、表名、字段列表等,字段属性包含名称、类型、注释、是否可搜索等。可通过修改模板自定义生成代码的风格和结构。