FastapiAdmin代码生成详细解释

一、概述

CRUD、参数校验、权限控制、菜单注册、前端页面,这些基本是机械重复的活。代码生成器(Code Generator)就是把这部分从手写里解放出来。

整个流程是:数据库元数据逆向 → 语义推断 → 配置持久化 → 模板渲染 → 全栈交付。一张物理表进去,后端五层接口加 Vue3 页面就出来了。

css 复制代码
flowchart LR
    A[物理数据库表\nMySQL / Postgres] -->|元数据逆向| B[元数据捕获层\nGenDBTable]
    B -->|智能推断与初始化| C[生成配置中心\nGenTable & GenTableColumn]
    C -->|开发者可视化微调| D[配置持久化\nORM元数据库]
    D -->|Jinja2 异步模板引擎| E[全栈代码生成]
    E --> F1[后端五层代码\nModel/Schema/CRUD/Service/Controller]
    E --> F2[前端交互代码\nVue3 SFC + TS API]
    E --> F3[权限与菜单 SQL\nRBAC 体系]

设计要点

  1. 字段名、注释、物理类型直接推断前端控件(Radio、Select、Upload、Editor)、查询方式(EQ、LIKE、BETWEEN)和类型映射,不用手动配。
  2. 生成的后端代码就是标准五层(Model → Schema → CRUD → Service → Controller),前端走组合式 API 规范。
  3. 支持主子表(Master-Detail)外键关联,级联保存、联合事务、嵌套 Schema、主从联动前端组件都会自动生成。
  4. 数据库字段变了可以做差异同步(DB Sync):先预览差异,增量补新字段,同时保留页面上已配置的业务属性。
  5. 三种交付方式:直接写进本地工程、批量打包 ZIP 下载、在线高亮预览。

二、模块划分与目录结构

代码生成模块的结构:

bash 复制代码
backend/app/api/v1/module_generator/gencode/
├── __init__.py
├── controller.py              # RESTful API 控制器(导入、预览、生成、同步等)
├── service.py                 # 核心编排服务(元数据抓取、文件 IO、ZIP 压缩、DB 同步)
├── crud.py                    # 元数据表 (gen_table/gen_table_column) 的持久层操作
├── model.py                   # 元数据持久化 ORM 模型定义
├── schema.py                  # Pydantic V2 请求/响应数据校验模型
├── gen_util.py                # 智能语义推断工具类(字段类型、UI控件、查询条件推断)
└── jinja2_template_util.py    # Jinja2 模板引擎封装、上下文构建与自定义过滤器

backend/templates/             # 统一代码生成模板库
├── python/                    # 后端 FastAPI 模板
│   ├── __init__.py.jinja2
│   ├── model.py.jinja2        # SQLAlchemy ORM 模型模板
│   ├── schema.py.jinja2       # Pydantic 校验模型模板
│   ├── crud.py.jinja2         # 基础持久化层模板
│   ├── service.py.jinja2      # 业务逻辑与事务编排模板
│   └── controller.py.jinja2   # HTTP 路由与权限切面模板
├── ts/
│   └── api.ts.jinja2          # 前端 TypeScript 接口与请求定义
└── vue/
    └── index.vue.jinja2       # Vue3 + Element Plus 全功能表格/表单页面

三、实现环节拆解

3.1 元数据逆向(Metadata Reverse Engineering)

先拿物理表的元数据。GenTableService 对不同数据库(MySQL、PostgreSQL 等)走 information_schema 或 SQL 驱动反查表与列信息。

rust 复制代码
sequenceDiagram
    autonumber
    actor Dev as 开发者 / 前端
    participant Ctrl as GenRouter (/gencode/import)
    participant Svc as GenTableService
    participant CRUD as GenTableCRUD
    participant DB as 业务数据库 (information_schema)

    Dev->>Ctrl: 提交待导入表名列表 [table_names]
    Ctrl->>Svc: import_gen_table()
    Svc->>DB: 查询表结构 (table_name, comment)
    Svc->>DB: 查询列元数据 (column_name, data_type, is_nullable, column_key...)
    DB-->>Svc: 返回物理表与列的原始元数据
    Svc->>Svc: 执行 GenUtils.init_table() 与 init_column_field() 推断
    Svc->>CRUD: 持久化至 gen_table 与 gen_table_column
    CRUD-->>Ctrl: 返回导入结果
    Ctrl-->>Dev: 提示导入成功

列信息里提取:列名、物理类型(column_type)、长度、默认值、是否主键(is_pk)、是否自增(is_increment)、是否可空(is_nullable)。

3.2 语义推断(Rule Inference Engine)

原始字段不能直接对应上层需要的维度:Python 类型、TS 类型、表单控件、列表是否显示、查询方式等。这些靠 gen_util.py 里的规则推断。

1. 模块与包路径推断

当导入一张表(如 tb_order_detail 或 gen_strategy_config)时:

  • 剥离前缀 :去掉 tb_、gen_ 等前缀。
  • 实体类名转驼峰 :tb_order_detail \rightarrow OrderDetail。
  • 模块与路径 :推断出 module_name = "order_detail"、package_name = "module_order_detail"。

2. 数据类型三重映射

物理数据库类型 → Python/SQLAlchemy 类型 → TypeScript 类型的映射:

物理数据库类型(DB Type) Python 类型 (Pydantic) SQLAlchemy 类型 TypeScript 类型
int, tinyint, bigint int Integer / BigInteger number
float, double, decimal float / Decimal Float / Numeric number
varchar, char, text str String(length) / Text string
datetime, timestamp datetime DateTime string
date date Date string
boolean, tinyint(1) bool Boolean boolean
json, jsonb dict / list JSON Record<string, any>

3. 字段名与 UI 控件

按命名惯例 + 类型特征推断控件:

bash 复制代码
# gen_util.py 核心推断逻辑片段
lower_name = column_name.lower()
if lower_name.endswith("status"):
    column.html_type = GenConstant.HTML_RADIO         # 状态类 -> 单选框组 (Radio)
elif lower_name.endswith("type") or lower_name.endswith("sex"):
    column.html_type = GenConstant.HTML_SELECT        # 类型/性别类 -> 下拉选择框 (Select)
elif lower_name.endswith("image") or lower_name.endswith("avatar"):
    column.html_type = GenConstant.HTML_IMAGE_UPLOAD  # 图片类 -> 图片上传控件
elif lower_name.endswith("file") or lower_name.endswith("url"):
    column.html_type = GenConstant.HTML_FILE_UPLOAD   # 文件类 -> 文件上传控件
elif lower_name.endswith("content") or lower_name.endswith("description"):
    column.html_type = GenConstant.HTML_EDITOR        # 内容/描述类 -> 富文本编辑器 (Editor)
elif cls.arrays_contains(GenConstant.COLUMNTYPE_TIME, data_type):
    column.html_type = GenConstant.HTML_DATETIME      # 时间类 -> 日期时间选择器 (DatePicker)

4. CRUD 行为与查询方式推断

  • 审计字段黑名单 :主键(id)和系统基类字段(created_time、updated_time、created_id、deleted_time 等)在新增、编辑、列表里默认屏蔽或只读。
  • 查询算子 :带 name、title 或字符串类型的默认 LIKE;数值、状态类默认 EQ;日期时间类默认 BETWEEN。

3.3 Jinja2 异步模板渲染

渲染引擎封装在 Jinja2TemplateUtil,开了 Jinja2 异步(enable_async=True),并发生成时不会阻塞事件循环。

1. 模板环境与自定义过滤器

ini 复制代码
cls._env = Environment(
    loader=FileSystemLoader(TEMPLATE_DIR),
    autoescape=False,
    trim_blocks=True,          # 消除模板标签产生的空行
    lstrip_blocks=True,        # 消除行首多余空白
    keep_trailing_newline=True,# 保留标准 POSIX 文件结尾换行
    enable_async=True,         # 开启原生异步渲染支持
)
cls._env.filters.update({
    "camel_to_snake": SnakeCaseUtil.camel_to_snake,
    "snake_to_camel": CamelCaseUtil.snake_to_camel,
    "get_sqlalchemy_type": cls.get_sqlalchemy_type,
    "python_to_ts_type": cls.python_type_to_ts_type,
})

2. 上下文变量(Context)

调 env.get_template().render_async(**context) 之前,先组装好上下文数据字典:

  • table:当前表的元数据配置对象。
  • columns:排好序的所有列对象列表。
  • pk_column / pk_python_type:主键列信息。
  • has_datetime / has_decimal:控制要不要生成 datetime、Decimal 的导入语句。
  • permission_prefix:按模块和业务名算出的权限前缀(如 order:detail)。
  • sub_table / sub_columns:配置了主子表才有,子表关联上下文。

3.4 模板设计

1. 模型模板(model.py.jinja2)

用 SQLAlchemy 2.0 的 Mapped / mapped_column 强类型注解,支持主子表 relationship 和外键级联:

python 复制代码
class {{ class_name }}Model(ModelMixin, UserMixin):
    """{{ function_name }}表"""
    __tablename__: str = '{{ table_name }}'
    __table_args__: dict[str, str] = {'comment': '{{ function_name }}'}

    {% for column in columns %}
    {% if column.column_name not in ['id', 'uuid', 'tenant_id', 'created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id'] %}
    {% set sqlalchemy_type = column|get_sqlalchemy_type %}
    {{ column.column_name }}: Mapped[{{ column.python_type }}{% if column.is_nullable %} | None{% endif %}] = mapped_column(
        {{ sqlalchemy_type }},
        {% if column.is_pk %}primary_key=True, {% endif %}
        {% if column.is_increment %}autoincrement=True, {% endif %}
        nullable={{ column.is_nullable }},
        comment='{{ column.column_comment }}'
    )
    {% endif %}
    {% endfor %}

    {% if table.sub %}
    {{ sub_rel_list_name }} = relationship('{{ sub_model_class_name }}', back_populates='{{ parent_rel_name }}', cascade='all, delete-orphan')
    {% endif %}

2. 控制器模板(controller.py.jinja2)

集成依赖注入、RBAC 权限校验(AuthPermission)、操作日志切面(OperationLogRoute)和统一响应:

ini 复制代码
{{ class_name }}Router = APIRouter(route_class=OperationLogRoute, prefix="/{{ module_name }}", tags=["{{ function_name }}模块"])

@{{ class_name }}Router.get("/list", summary="分页查询{{ function_name }}", response_model=ResponseSchema[PageResultSchema[{{ class_name }}OutSchema]])
async def get_obj_list_controller(
    auth: Annotated[AuthSchema, Depends(AuthPermission(["{{ permission_prefix }}:query"]))],
    page: Annotated[PaginationQueryParam, Depends()],
    search: Annotated[{{ class_name }}QueryParam, Query()],
    db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
    service = {{ class_name }}Service(auth, db)
    result_dict = await service.page(page_no=page.page_no, page_size=page.page_size, search=search, order_by=page.order_by)
    return SuccessResponse(data=result_dict, msg="查询{{ function_name }}列表成功")

3.5 交付与持久化

三种交付方式:

css 复制代码
graph TD
    A[模板渲染完成\nMemory Buffer] --> B{选择交付模式}
    B -->|直接生成到项目| C[本地文件系统 I/O\nWrite to backend/ & frontend/]
    B -->|下载代码包| D[内存 ZIP 压缩流\nio.BytesIO + StreamingResponse]
    B -->|在线代码预览| E[JSON 返回字典结构\nMonaco Editor / Highlight.js]

1. 本地写入(generate_code)

按 package_name、module_name 把渲染结果直接写进后端 API 目录和前端 src/views:

  • 创建缺失的中间目录;
  • 安全写入 model.py、schema.py、crud.py、service.py、controller.py、api.ts、index.vue;
  • 路径分隔符自动处理,Windows / Linux 都能跑。

2. ZIP 打包(batch_gen_code)

多人协作或想先审查再合并的场景,用 zipfile.ZipFile 在内存里打包 io.BytesIO(),遍历选中的表分层归档:

scss 复制代码
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
    for template_file, output_path in zip(templates, output_paths):
        render_content = await env.get_template(template_file).render_async(**context)
        zip_file.writestr(output_path, render_content)
zip_buffer.seek(0)
# 返回流式响应
return bytes2file_response(zip_buffer.getvalue())

3.6 增量同步与差异检测(Diff & Sync)

迭代中数据库经常加列、改长度、删废弃列。如果直接重新生成,页面上配好的表单类型、查询规则、字典设置就全丢了。

所以做了差异比对 + 增量同步:

css 复制代码
flowchart TD
    A[触发库表同步: sync_db] --> B[读取物理数据库当前最新列]
    B --> C[读取 gen_table_column 已持久化的配置列]
    C --> D[执行集合比对 Diff]
    
    D -->|新增物理列| E[执行 init_column_field 推断并 INSERT]
    D -->|已存在列| F[仅更新物理属性 data_type/length/comment\n保留用户配置的 html_type/query_type/dict_type]
    D -->|数据库已删除列| G[从 gen_table_column 中 DELETE]
    
    E & F & G --> H[同步完成,更新 gen_table 状态]

差异预览机制(Sync Preview)

正式同步前,先调 /sync_db/preview/{table_name} 拿差异概览(GenSyncPreviewSchema):

  • added_columns:待新增的字段列表;
  • modified_columns:字段类型/注释/长度发生变化的字段列表;
  • deleted_columns:在物理库中已不存在的冗余字段列表。

四、主子表(Master-Detail)生成

不只支持单表,还支持一对多主子表(比如订单 tb_order + 明细 tb_order_item,或策略表 + 运行日志表)。

4.1 关联配置

GenTableModel 里定义关联元信息:

  • sub_table_name:关联子表的物理表名。
  • sub_table_fk_name:子表中指向主表主键的外键列名。

4.2 级联代码生成机制

  1. 主表 ORM :自动生成 relationship(..., cascade='all, delete-orphan') 关系引用。

  2. 子表 ORM :自动在指定的外键列上添加 ForeignKey('parent_table.id', ondelete='CASCADE')。

  3. Pydantic Schema:

    • 主表 Create/Update Schema 自动嵌套子表 Schema 列表:items: list[OrderItemCreateSchema] | None = []。
  4. Service 事务层:

    • create():同一事务里先插主表拿到主键 ID,再给子表批量设置外键落库。
    • update():比对子表主键,存在则更新、不存在则插入、缺失则删除。
  5. 前端 UI:

    • 主表详情/编辑表单里渲染可增删行的子表格。

五、使用建议

5.1 标准流程

markdown 复制代码
1. 数据库建模规范设计(必须包含主键、注释、状态等标准字段)
   ↓
2. 在系统界面「代码生成」中点击「导入」,选择业务表
   ↓
3. 在可视化抽屉中微调:
   - 检查模块名(module_name)与业务名(business_name)
   - 配置字典绑定(如 status 绑定 sys_normal_disable)
   - 设置查询方式(模糊查询 / 精确匹配 / 时间范围)
   - 若为主从业务,配置关联子表与外键
   ↓
4. 点击「代码预览」,检查 Python 与 Vue 代码语法
   ↓
5. 点击「生成代码」,代码自动同步至本地项目目录
   ↓
6. 自动/手动执行生成的菜单 SQL,刷新前端即可查看完整功能模块

5.2 字段命名规范推荐表

想让自动推断准一点,建表时按下面这些命名来:

字段命名模式 推荐物理类型 自动推断结果 界面呈现形式
*_status tinyint / int html_type: radio, query_type: EQ 单选框组(支持绑定字典)
*_type, *_category varchar / int html_type: select, query_type: EQ 下拉选择器
*_name, *_title varchar html_type: input, query_type: LIKE 文本输入框 + 模糊搜索
*_time, *_date datetime / date html_type: datetime, query_type: BETWEEN 日期范围选择器
*_image, *_avatar varchar html_type: imageUpload 图片上传与缩略图展示
*_file, *_attachment varchar html_type: fileUpload 文件上传与下载链接
*_content, *_remark text html_type: editor / textarea 富文本编辑器 / 多行文本域

六、总结

整体链路就是"表 → 推断 → 配置 → 渲染 → 交付"。字段命名规范点、注释写清楚,生成出来的东西基本不用改;主子表和差异同步,用到的时候翻前面的对应章节就行。

相关推荐
杨利杰YJlio1 小时前
ITSK 万能驱动 26V5:新驱动批量更新
前端·javascript·后端
deli0071 小时前
克拉德尼图形:调一下频率,沙子自己排成对称花纹
前端
imDwAaY1 小时前
6篇文章讲清楚Git:Git 团队开发实践:从 Pull Request 到版本发布 (6/6)
git·后端
DevUp1 小时前
老站往哪搬:织梦、帝国、PHPCMS 的出路
后端·php·cms
光影少年1 小时前
TurboModule原理
前端·前端框架·turbopack
王中阳Go1 小时前
甲骨文裁3万、DeepSeek却招150人:后端程序员往哪走,我把这批JD拆了一遍
后端
亿元程序员1 小时前
自从有了 AI,我就再也不想拼 UI 了……
前端
数据狐(Datafox)1 小时前
淘宝商品详情API实战:多语言代购商城自动同步数据完整方案
开发语言·前端·数据库·爬虫·json
打工仔折腾 AI2 小时前
蓝耘元生代实测:用WorkBuddy与TextIn xParse拆解43页建模论文
人工智能·后端·python·数学建模·langchain·ai agent 实战