mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 20:55:14 +00:00
chore: 完成多批次代码优化与重构
- 重构工作流模块目录结构,迁移代码文件 - 修复类型断言空值安全问题,添加 ! 操作符 - 优化样式类名,替换 flex-cc 为标准 flex 工具类 - 更新路由标签简化文案,移除冗余注释 - 调整 ruff 配置,放宽行长度限制 - 更新 README 与多语言文案,优化项目描述 - 修复表单、图表组件的类型与样式问题 - 简化搜索表单、数据卡片的布局代码
This commit is contained in:
@@ -21,7 +21,7 @@ from .schema import (
|
||||
)
|
||||
from .service import GenTableService
|
||||
|
||||
GenRouter = APIRouter(route_class=OperationLogRoute, prefix="/gencode", tags=["开发工具/代码生成"])
|
||||
GenRouter = APIRouter(route_class=OperationLogRoute, prefix="/gencode", tags=["代码生成"])
|
||||
|
||||
|
||||
@GenRouter.get(
|
||||
@@ -111,9 +111,7 @@ async def import_gen_table_controller(
|
||||
返回:
|
||||
- JSONResponse: 包含导入结果和导入的表结构列表的JSON响应
|
||||
"""
|
||||
add_gen_table_list = await GenTableService.get_gen_db_table_list_by_name_service(
|
||||
auth, table_names
|
||||
)
|
||||
add_gen_table_list = await GenTableService.get_gen_db_table_list_by_name_service(auth, table_names)
|
||||
result = await GenTableService.import_gen_table_service(auth, add_gen_table_list)
|
||||
return SuccessResponse(msg="导入表结构成功", data=result)
|
||||
|
||||
|
||||
@@ -34,9 +34,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
"""
|
||||
super().__init__(model=GenTableModel, auth=auth)
|
||||
|
||||
async def get_gen_table_by_id(
|
||||
self, table_id: int, preload: list | None = None
|
||||
) -> GenTableModel | None:
|
||||
async def get_gen_table_by_id(self, table_id: int, preload: list | None = None) -> GenTableModel | None:
|
||||
"""
|
||||
根据业务表ID获取需要生成的业务表信息。
|
||||
|
||||
@@ -49,9 +47,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
"""
|
||||
return await self.get(id=table_id, preload=preload)
|
||||
|
||||
async def get_gen_table_by_name(
|
||||
self, table_name: str, preload: list | None = None
|
||||
) -> GenTableModel | None:
|
||||
async def get_gen_table_by_name(self, table_name: str, preload: list | None = None) -> GenTableModel | None:
|
||||
"""
|
||||
根据业务表名称获取需要生成的业务表信息。
|
||||
|
||||
@@ -160,11 +156,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
for table_name in table_names:
|
||||
try:
|
||||
table_comment = inspector.get_table_comment(table_name)
|
||||
comment = (
|
||||
table_comment.get("text", "")
|
||||
if isinstance(table_comment, dict)
|
||||
else table_comment
|
||||
)
|
||||
comment = table_comment.get("text", "") if isinstance(table_comment, dict) else table_comment
|
||||
table_comment = comment or ""
|
||||
except Exception as e:
|
||||
logger.warning(f"获取表 {table_name} 的注释失败: {e}")
|
||||
@@ -173,18 +165,10 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
# 统一处理 search 为 None 的情况,避免重复判断
|
||||
if search:
|
||||
# 表名过滤:忽略大小写,支持模糊匹配
|
||||
if (
|
||||
search.table_name
|
||||
and search.table_name[1]
|
||||
and search.table_name[1].lower() not in table_name.lower()
|
||||
):
|
||||
if search.table_name and search.table_name[1] and search.table_name[1].lower() not in table_name.lower():
|
||||
continue
|
||||
# 表注释过滤:忽略大小写,支持模糊匹配;table_comment 为 None 时视为空字符串
|
||||
if (
|
||||
search.table_comment
|
||||
and search.table_comment[1]
|
||||
and search.table_comment[1] not in table_comment
|
||||
):
|
||||
if search.table_comment and search.table_comment[1] and search.table_comment[1] not in table_comment:
|
||||
continue
|
||||
|
||||
table_info = {
|
||||
@@ -248,12 +232,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
params["comment_kw"] = f"%{comment_kw}%"
|
||||
|
||||
count_sql = text(f"SELECT COUNT(1) AS cnt FROM information_schema.tables {where_sql}")
|
||||
rows_sql = text(
|
||||
"SELECT table_name, table_comment "
|
||||
f"FROM information_schema.tables {where_sql} "
|
||||
"ORDER BY table_name ASC "
|
||||
"LIMIT :limit OFFSET :offset"
|
||||
)
|
||||
rows_sql = text(f"SELECT table_name, table_comment FROM information_schema.tables {where_sql} ORDER BY table_name ASC LIMIT :limit OFFSET :offset")
|
||||
total_res = await self.auth.db.execute(count_sql, params)
|
||||
total = int(total_res.scalar() or 0)
|
||||
res = await self.auth.db.execute(rows_sql, params)
|
||||
@@ -275,9 +254,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
# PostgreSQL
|
||||
if db_type in {"postgresql", "postgres"}:
|
||||
# pg_description 需要通过 objsubid=0 获取 table comment
|
||||
where_sql = (
|
||||
"WHERE n.nspname NOT IN ('pg_catalog','information_schema') AND c.relkind = 'r'"
|
||||
)
|
||||
where_sql = "WHERE n.nspname NOT IN ('pg_catalog','information_schema') AND c.relkind = 'r'"
|
||||
params = {"offset": offset, "limit": limit}
|
||||
if name_kw:
|
||||
where_sql += " AND c.relname ILIKE :name_kw"
|
||||
@@ -286,18 +263,9 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
where_sql += " AND COALESCE(d.description,'') ILIKE :comment_kw"
|
||||
params["comment_kw"] = f"%{comment_kw}%"
|
||||
|
||||
base_from = (
|
||||
"FROM pg_catalog.pg_class c "
|
||||
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
|
||||
"LEFT JOIN pg_catalog.pg_description d ON d.objoid = c.oid AND d.objsubid = 0 "
|
||||
)
|
||||
base_from = "FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_description d ON d.objoid = c.oid AND d.objsubid = 0 "
|
||||
count_sql = text(f"SELECT COUNT(1) AS cnt {base_from} {where_sql}")
|
||||
rows_sql = text(
|
||||
"SELECT c.relname AS table_name, COALESCE(d.description,'') AS table_comment "
|
||||
f"{base_from} {where_sql} "
|
||||
"ORDER BY c.relname ASC "
|
||||
"LIMIT :limit OFFSET :offset"
|
||||
)
|
||||
rows_sql = text(f"SELECT c.relname AS table_name, COALESCE(d.description,'') AS table_comment {base_from} {where_sql} ORDER BY c.relname ASC LIMIT :limit OFFSET :offset")
|
||||
total_res = await self.auth.db.execute(count_sql, params)
|
||||
total = int(total_res.scalar() or 0)
|
||||
res = await self.auth.db.execute(rows_sql, params)
|
||||
@@ -347,11 +315,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
continue
|
||||
try:
|
||||
table_comment = inspector.get_table_comment(table_name)
|
||||
comment = (
|
||||
table_comment.get("text", "")
|
||||
if isinstance(table_comment, dict)
|
||||
else (table_comment or "")
|
||||
)
|
||||
comment = table_comment.get("text", "") if isinstance(table_comment, dict) else (table_comment or "")
|
||||
except Exception as e:
|
||||
logger.warning(f"获取表 {table_name} 的注释失败: {e}")
|
||||
comment = ""
|
||||
@@ -399,11 +363,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
return ""
|
||||
try:
|
||||
table_comment = inspector.get_table_comment(table_name)
|
||||
comment = (
|
||||
table_comment.get("text", "")
|
||||
if isinstance(table_comment, dict)
|
||||
else (table_comment or "")
|
||||
)
|
||||
comment = table_comment.get("text", "") if isinstance(table_comment, dict) else (table_comment or "")
|
||||
return comment or ""
|
||||
except Exception as e:
|
||||
logger.warning(f"获取表 {table_name} 的注释失败: {e}")
|
||||
@@ -463,9 +423,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
# 获取主键信息
|
||||
try:
|
||||
pk_constraint = inspector.get_pk_constraint(table_name)
|
||||
primary_keys = (
|
||||
set(pk_constraint.get("constrained_columns", [])) if pk_constraint else set()
|
||||
)
|
||||
primary_keys = set(pk_constraint.get("constrained_columns", [])) if pk_constraint else set()
|
||||
except Exception:
|
||||
primary_keys = set()
|
||||
|
||||
@@ -513,9 +471,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
|
||||
return columns_list
|
||||
|
||||
async def get_gen_table_column_by_id(
|
||||
self, id: int, preload: list | None = None
|
||||
) -> GenTableColumnModel | None:
|
||||
async def get_gen_table_column_by_id(self, id: int, preload: list | None = None) -> GenTableColumnModel | None:
|
||||
"""根据业务表字段ID获取业务表字段信息。
|
||||
|
||||
参数:
|
||||
@@ -527,9 +483,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
"""
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_gen_table_column_list_by_table_id(
|
||||
self, table_id: int, preload: list | None = None
|
||||
) -> GenTableColumnModel | None:
|
||||
async def get_gen_table_column_list_by_table_id(self, table_id: int, preload: list | None = None) -> GenTableColumnModel | None:
|
||||
"""根据业务表ID获取业务表字段列表信息。
|
||||
|
||||
参数:
|
||||
@@ -559,9 +513,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
"""
|
||||
return await self.list(search={"table_id": table_id}, order_by=order_by, preload=preload)
|
||||
|
||||
async def get_gen_db_table_columns_by_name(
|
||||
self, table_name: str | None
|
||||
) -> list[GenTableColumnOutSchema]:
|
||||
async def get_gen_db_table_columns_by_name(self, table_name: str | None) -> list[GenTableColumnOutSchema]:
|
||||
"""
|
||||
根据业务表名称获取业务表字段列表信息。
|
||||
|
||||
@@ -610,9 +562,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_gen_table_column_crud(
|
||||
self, data: GenTableColumnSchema
|
||||
) -> GenTableColumnModel | None:
|
||||
async def create_gen_table_column_crud(self, data: GenTableColumnSchema) -> GenTableColumnModel | None:
|
||||
"""创建业务表字段。
|
||||
|
||||
参数:
|
||||
@@ -623,9 +573,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
"""
|
||||
return await self.create(data=data)
|
||||
|
||||
async def update_gen_table_column_crud(
|
||||
self, id: int, data: GenTableColumnSchema
|
||||
) -> GenTableColumnModel | None:
|
||||
async def update_gen_table_column_crud(self, id: int, data: GenTableColumnSchema) -> GenTableColumnModel | None:
|
||||
"""更新业务表字段。
|
||||
|
||||
参数:
|
||||
|
||||
@@ -16,236 +16,76 @@ class GenTableModel(ModelMixin, TenantMixin, UserMixin):
|
||||
__table_args__: dict[str, str] = {"comment": "代码生成表"}
|
||||
__loader_options__: list[str] = ["columns", "created_by", "updated_by", "deleted_by"]
|
||||
|
||||
table_name: Mapped[str] = mapped_column(
|
||||
String(200), nullable=False, default="", comment="表名称"
|
||||
)
|
||||
table_name: Mapped[str] = mapped_column(String(200), nullable=False, default="", comment="表名称")
|
||||
table_comment: Mapped[str | None] = mapped_column(String(500), nullable=True, comment="表描述")
|
||||
|
||||
class_name: Mapped[str] = mapped_column(
|
||||
String(100), nullable=False, default="", comment="实体类名称"
|
||||
)
|
||||
package_name: Mapped[str | None] = mapped_column(
|
||||
String(100), nullable=True, comment="生成包路径"
|
||||
)
|
||||
class_name: Mapped[str] = mapped_column(String(100), nullable=False, default="", comment="实体类名称")
|
||||
package_name: Mapped[str | None] = mapped_column(String(100), nullable=True, comment="生成包路径")
|
||||
module_name: Mapped[str | None] = mapped_column(String(30), nullable=True, comment="生成模块名")
|
||||
business_name: Mapped[str | None] = mapped_column(
|
||||
String(30), nullable=True, comment="生成业务名"
|
||||
)
|
||||
function_name: Mapped[str | None] = mapped_column(
|
||||
String(100), nullable=True, comment="生成功能名"
|
||||
)
|
||||
|
||||
sub_table_name: Mapped[str | None] = mapped_column(
|
||||
String(64),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(settings.DATABASE_TYPE),
|
||||
comment="关联子表的表名",
|
||||
)
|
||||
sub_table_fk_name: Mapped[str | None] = mapped_column(
|
||||
String(64),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(settings.DATABASE_TYPE),
|
||||
comment="子表关联的外键名",
|
||||
)
|
||||
|
||||
business_name: Mapped[str | None] = mapped_column(String(30), nullable=True, comment="生成业务名")
|
||||
function_name: Mapped[str | None] = mapped_column(String(100), nullable=True, comment="生成功能名")
|
||||
sub_table_name: Mapped[str | None] = mapped_column(String(64), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(settings.DATABASE_TYPE), comment="关联子表的表名")
|
||||
sub_table_fk_name: Mapped[str | None] = mapped_column(String(64), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(settings.DATABASE_TYPE), comment="子表关联的外键名")
|
||||
parent_menu_id: Mapped[int | None] = mapped_column(Integer, nullable=True, comment="父菜单ID")
|
||||
|
||||
# 关联关系
|
||||
columns: Mapped[list["GenTableColumnModel"]] = relationship(
|
||||
order_by="GenTableColumnModel.sort",
|
||||
back_populates="table",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
columns: Mapped[list["GenTableColumnModel"]] = relationship(order_by="GenTableColumnModel.sort", back_populates="table", cascade="all, delete-orphan")
|
||||
status: Mapped[int] = mapped_column(Integer, default=0, nullable=False, comment="状态(0:启动 1:停用)", index=True)
|
||||
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注")
|
||||
|
||||
@validates("table_name")
|
||||
def validate_table_name(self, key: str, table_name: str) -> str:
|
||||
"""
|
||||
验证表名非空并去首尾空格。
|
||||
|
||||
参数:
|
||||
- key (str): 字段名。
|
||||
- table_name (str): 表名。
|
||||
|
||||
返回:
|
||||
- str: 规范化后的表名。
|
||||
|
||||
异常:
|
||||
- ValueError: 表名为空时抛出。
|
||||
"""
|
||||
"""验证表名非空并去首尾空格。"""
|
||||
if not table_name or not table_name.strip():
|
||||
raise ValueError("表名称不能为空")
|
||||
return table_name.strip()
|
||||
|
||||
@validates("class_name")
|
||||
def validate_class_name(self, key: str, class_name: str) -> str:
|
||||
"""
|
||||
验证实体类名非空并去首尾空格。
|
||||
|
||||
参数:
|
||||
- key (str): 字段名。
|
||||
- class_name (str): 类名。
|
||||
|
||||
返回:
|
||||
- str: 规范化后的类名。
|
||||
|
||||
异常:
|
||||
- ValueError: 类名为空时抛出。
|
||||
"""
|
||||
"""验证实体类名非空并去首尾空格。"""
|
||||
if not class_name or not class_name.strip():
|
||||
raise ValueError("实体类名称不能为空")
|
||||
return class_name.strip()
|
||||
|
||||
|
||||
class GenTableColumnModel(ModelMixin, TenantMixin, UserMixin):
|
||||
"""
|
||||
代码生成表字段
|
||||
|
||||
数据隔离策略:
|
||||
- 继承自GenTableModel的隔离级别
|
||||
- 不需要customer_id
|
||||
|
||||
用于存储代码生成器的字段配置
|
||||
"""
|
||||
"""代码生成表字段"""
|
||||
|
||||
__tablename__: str = "gen_table_column"
|
||||
__table_args__: dict[str, str] = {"comment": "代码生成表字段"}
|
||||
__loader_options__: list[str] = ["created_by", "updated_by", "deleted_by"]
|
||||
|
||||
# 数据库设计表字段
|
||||
column_name: Mapped[str] = mapped_column(String(200), nullable=False, comment="列名称")
|
||||
column_comment: Mapped[str | None] = mapped_column(String(500), nullable=True, comment="列描述")
|
||||
column_type: Mapped[str] = mapped_column(String(100), nullable=False, comment="列类型")
|
||||
column_length: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="列长度")
|
||||
column_default: Mapped[str | None] = mapped_column(
|
||||
String(200), nullable=True, comment="列默认值"
|
||||
)
|
||||
is_pk: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=False,
|
||||
server_default=expression.false(),
|
||||
comment="是否主键",
|
||||
)
|
||||
is_increment: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=False,
|
||||
server_default=expression.false(),
|
||||
comment="是否自增",
|
||||
)
|
||||
is_nullable: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=True,
|
||||
server_default=expression.true(),
|
||||
comment="是否允许为空",
|
||||
)
|
||||
is_unique: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=False,
|
||||
server_default=expression.false(),
|
||||
comment="是否唯一",
|
||||
)
|
||||
|
||||
# Python字段映射
|
||||
python_type: Mapped[str | None] = mapped_column(
|
||||
String(100), nullable=True, comment="Python类型"
|
||||
)
|
||||
python_field: Mapped[str | None] = mapped_column(
|
||||
String(200), nullable=True, comment="Python字段名"
|
||||
)
|
||||
|
||||
# 序列化配置
|
||||
is_insert: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=True,
|
||||
server_default=expression.true(),
|
||||
comment="是否为新增字段",
|
||||
)
|
||||
is_edit: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=True,
|
||||
server_default=expression.true(),
|
||||
comment="是否编辑字段",
|
||||
)
|
||||
is_list: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=True,
|
||||
server_default=expression.true(),
|
||||
comment="是否列表字段",
|
||||
)
|
||||
is_query: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=False,
|
||||
server_default=expression.false(),
|
||||
comment="是否查询字段",
|
||||
)
|
||||
query_type: Mapped[str | None] = mapped_column(
|
||||
String(50), nullable=True, default=None, comment="查询方式"
|
||||
)
|
||||
|
||||
# 前端展示配置
|
||||
html_type: Mapped[str | None] = mapped_column(
|
||||
String(100), nullable=True, default="input", comment="显示类型"
|
||||
)
|
||||
dict_type: Mapped[str | None] = mapped_column(
|
||||
String(200), nullable=True, default="", comment="字典类型"
|
||||
)
|
||||
|
||||
# 排序和扩展配置
|
||||
column_default: Mapped[str | None] = mapped_column(String(200), nullable=True, comment="列默认值")
|
||||
is_pk: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=expression.false(), comment="是否主键")
|
||||
is_increment: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=expression.false(), comment="是否自增")
|
||||
is_nullable: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default=expression.true(), comment="是否允许为空")
|
||||
is_unique: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=expression.false(), comment="是否唯一")
|
||||
python_type: Mapped[str | None] = mapped_column(String(100), nullable=True, comment="Python类型")
|
||||
python_field: Mapped[str | None] = mapped_column(String(200), nullable=True, comment="Python字段名")
|
||||
is_insert: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default=expression.true(), comment="是否为新增字段")
|
||||
is_edit: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default=expression.true(), comment="是否编辑字段")
|
||||
is_list: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default=expression.true(), comment="是否列表字段")
|
||||
is_query: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=expression.false(), comment="是否查询字段")
|
||||
query_type: Mapped[str | None] = mapped_column(String(50), nullable=True, default=None, comment="查询方式")
|
||||
html_type: Mapped[str | None] = mapped_column(String(100), nullable=True, default="input", comment="前端显示类型")
|
||||
dict_type: Mapped[str | None] = mapped_column(String(200), nullable=True, default="", comment="前端对应字典类型")
|
||||
sort: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="排序")
|
||||
|
||||
# 归属关系
|
||||
table_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("gen_table.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="归属表编号",
|
||||
)
|
||||
|
||||
# 关联关系
|
||||
table_id: Mapped[int] = mapped_column(Integer, ForeignKey("gen_table.id", ondelete="CASCADE"), nullable=False, index=True, comment="归属表编号")
|
||||
table: Mapped["GenTableModel"] = relationship(back_populates="columns")
|
||||
status: Mapped[int] = mapped_column(Integer, default=0, nullable=False, comment="状态(0:启动 1:停用)", index=True)
|
||||
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注")
|
||||
|
||||
@validates("column_name")
|
||||
def validate_column_name(self, key: str, column_name: str) -> str:
|
||||
"""
|
||||
验证列名非空并去首尾空格。
|
||||
|
||||
参数:
|
||||
- key (str): 字段名。
|
||||
- column_name (str): 列名。
|
||||
|
||||
返回:
|
||||
- str: 规范化后的列名。
|
||||
|
||||
异常:
|
||||
- ValueError: 列名为空时抛出。
|
||||
"""
|
||||
"""验证列名非空并去首尾空格。"""
|
||||
if not column_name or not column_name.strip():
|
||||
raise ValueError("列名称不能为空")
|
||||
return column_name.strip()
|
||||
|
||||
@validates("column_type")
|
||||
def validate_column_type(self, key: str, column_type: str) -> str:
|
||||
"""
|
||||
验证列类型非空并去首尾空格。
|
||||
|
||||
参数:
|
||||
- key (str): 字段名。
|
||||
- column_type (str): 列类型字符串。
|
||||
|
||||
返回:
|
||||
- str: 规范化后的列类型。
|
||||
|
||||
异常:
|
||||
- ValueError: 列类型为空时抛出。
|
||||
"""
|
||||
"""验证列类型非空并去首尾空格。"""
|
||||
if not column_type or not column_type.strip():
|
||||
raise ValueError("列类型不能为空")
|
||||
return column_type.strip()
|
||||
|
||||
@@ -71,9 +71,7 @@ class GenTableService:
|
||||
"""代码生成业务表服务层"""
|
||||
|
||||
@classmethod
|
||||
async def _effective_package_name(
|
||||
cls, auth: AuthSchema, parent_catalog_id: int | None, package_name: str | None
|
||||
) -> str:
|
||||
async def _effective_package_name(cls, auth: AuthSchema, parent_catalog_id: int | None, package_name: str | None) -> str:
|
||||
"""根据「是否选择上级目录」计算最终包名(分系统根目录)。
|
||||
|
||||
规则(与你描述一致):
|
||||
@@ -105,9 +103,7 @@ class GenTableService:
|
||||
return pn if pn.startswith("module_") else f"module_{pn}"
|
||||
|
||||
@classmethod
|
||||
async def _assert_parent_menu_is_catalog(
|
||||
cls, auth: AuthSchema, parent_menu_id: int | None
|
||||
) -> None:
|
||||
async def _assert_parent_menu_is_catalog(cls, auth: AuthSchema, parent_menu_id: int | None) -> None:
|
||||
"""上级菜单仅允许目录:与前端树只展示目录一致,避免挂到菜单/按钮下。"""
|
||||
if parent_menu_id is None:
|
||||
return
|
||||
@@ -120,9 +116,7 @@ class GenTableService:
|
||||
raise CustomException(msg="上级菜单须选择目录类型")
|
||||
|
||||
@classmethod
|
||||
def _menu_route_first_segment(
|
||||
cls, parent_catalog_id: int | None, package_name: str, module_name: str | None
|
||||
) -> str:
|
||||
def _menu_route_first_segment(cls, parent_catalog_id: int | None, package_name: str, module_name: str | None) -> str:
|
||||
"""前端页面路由首段(与菜单 ``route_path`` 第一段一致)。
|
||||
|
||||
统一规则:始终使用分系统包名 ``module_xxx`` 作为路由首段。
|
||||
@@ -135,9 +129,7 @@ class GenTableService:
|
||||
return pn if pn.startswith("module_") else f"module_{pn}"
|
||||
|
||||
@classmethod
|
||||
def _catalog_menu_dir_key(
|
||||
cls, parent_catalog_id: int | None, package_name: str, module_name: str | None
|
||||
) -> str:
|
||||
def _catalog_menu_dir_key(cls, parent_catalog_id: int | None, package_name: str, module_name: str | None) -> str:
|
||||
"""菜单上「模块目录」节点的 name(与路由第一段 package 独立)。
|
||||
|
||||
统一为 **目录 → 菜单 → 按钮**:
|
||||
@@ -172,17 +164,11 @@ class GenTableService:
|
||||
dir_key = cls._catalog_menu_dir_key(parent_catalog_id, pn, module_name)
|
||||
|
||||
if parent_catalog_id is not None:
|
||||
existing = await menu_crud.get(
|
||||
name=dir_key, type=_MENU_TYPE_CATALOG, parent_id=parent_catalog_id
|
||||
)
|
||||
existing = await menu_crud.get(name=dir_key, type=_MENU_TYPE_CATALOG, parent_id=parent_catalog_id)
|
||||
else:
|
||||
existing = await menu_crud.get(
|
||||
name=dir_key, type=_MENU_TYPE_CATALOG, parent_id=(QueueEnum.none.value, None)
|
||||
)
|
||||
existing = await menu_crud.get(name=dir_key, type=_MENU_TYPE_CATALOG, parent_id=(QueueEnum.none.value, None))
|
||||
if existing:
|
||||
logger.info(
|
||||
f"代码生成:复用模块目录菜单 id={existing.id} name={dir_key!r} parent={parent_catalog_id!r}"
|
||||
)
|
||||
logger.info(f"代码生成:复用模块目录菜单 id={existing.id} name={dir_key!r} parent={parent_catalog_id!r}")
|
||||
return int(existing.id)
|
||||
|
||||
route_first = cls._menu_route_first_segment(parent_catalog_id, pn, module_name)
|
||||
@@ -213,9 +199,7 @@ class GenTableService:
|
||||
description="模块目录(代码生成)",
|
||||
)
|
||||
)
|
||||
logger.info(
|
||||
f"代码生成:新建模块目录菜单 id={created.id} name={dir_key!r} under_parent={parent_catalog_id!r}"
|
||||
)
|
||||
logger.info(f"代码生成:新建模块目录菜单 id={created.id} name={dir_key!r} under_parent={parent_catalog_id!r}")
|
||||
return int(created.id)
|
||||
|
||||
@classmethod
|
||||
@@ -257,9 +241,7 @@ class GenTableService:
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def get_gen_table_list_service(
|
||||
cls, auth: AuthSchema, search: GenTableQueryParam
|
||||
) -> list[dict]:
|
||||
async def get_gen_table_list_service(cls, auth: AuthSchema, search: GenTableQueryParam) -> list[dict]:
|
||||
"""
|
||||
获取代码生成业务表列表信息。
|
||||
|
||||
@@ -308,9 +290,7 @@ class GenTableService:
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def get_gen_db_table_list_service(
|
||||
cls, auth: AuthSchema, search: GenTableQueryParam
|
||||
) -> list[Any]:
|
||||
async def get_gen_db_table_list_service(cls, auth: AuthSchema, search: GenTableQueryParam) -> list[Any]:
|
||||
"""获取数据库表列表。
|
||||
|
||||
参数:
|
||||
@@ -345,9 +325,7 @@ class GenTableService:
|
||||
- dict[str, Any]: 含 items、total、has_next 等字段。
|
||||
"""
|
||||
offset = (page_no - 1) * page_size
|
||||
items, total = await GenTableCRUD(auth=auth).get_db_table_page(
|
||||
search=search, offset=offset, limit=page_size
|
||||
)
|
||||
items, total = await GenTableCRUD(auth=auth).get_db_table_page(search=search, offset=offset, limit=page_size)
|
||||
return {
|
||||
"items": items,
|
||||
"total": total,
|
||||
@@ -358,9 +336,7 @@ class GenTableService:
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def get_gen_db_table_list_by_name_service(
|
||||
cls, auth: AuthSchema, table_names: list[str]
|
||||
) -> list[GenTableOutSchema]:
|
||||
async def get_gen_db_table_list_by_name_service(cls, auth: AuthSchema, table_names: list[str]) -> list[GenTableOutSchema]:
|
||||
"""根据表名称组获取数据库表信息。
|
||||
|
||||
参数:
|
||||
@@ -373,17 +349,13 @@ class GenTableService:
|
||||
gen_db_table_list_result = await GenTableCRUD(auth).get_db_table_list_by_names(table_names)
|
||||
|
||||
# 修复:将GenDBTableSchema对象转换为字典后再传递给GenTableOutSchema
|
||||
result = [
|
||||
GenTableOutSchema(**gen_table.model_dump()) for gen_table in gen_db_table_list_result
|
||||
]
|
||||
result = [GenTableOutSchema(**gen_table.model_dump()) for gen_table in gen_db_table_list_result]
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def import_gen_table_service(
|
||||
cls, auth: AuthSchema, gen_table_list: list[GenTableOutSchema]
|
||||
) -> bool:
|
||||
async def import_gen_table_service(cls, auth: AuthSchema, gen_table_list: list[GenTableOutSchema]) -> bool:
|
||||
"""导入表结构到生成器。
|
||||
|
||||
参数:
|
||||
@@ -398,9 +370,7 @@ class GenTableService:
|
||||
raise CustomException(msg="导入的表结构不能为空")
|
||||
try:
|
||||
for table in gen_table_list:
|
||||
_row = {
|
||||
k: v for k, v in table.model_dump().items() if k in GenTableSchema.model_fields
|
||||
}
|
||||
_row = {k: v for k, v in table.model_dump().items() if k in GenTableSchema.model_fields}
|
||||
cls.normalize_and_validate_master_sub(GenTableSchema.model_validate(_row))
|
||||
table_name = table.table_name
|
||||
# 检查表是否已存在
|
||||
@@ -410,12 +380,8 @@ class GenTableService:
|
||||
GenUtils.init_table(table)
|
||||
if not table.columns:
|
||||
table.columns = []
|
||||
add_gen_table = await GenTableCRUD(auth).add_gen_table(
|
||||
GenTableSchema.model_validate(table.model_dump())
|
||||
)
|
||||
gen_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(
|
||||
table_name
|
||||
)
|
||||
add_gen_table = await GenTableCRUD(auth).add_gen_table(GenTableSchema.model_validate(table.model_dump()))
|
||||
gen_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name)
|
||||
if len(gen_table_columns) > 0:
|
||||
table.id = add_gen_table.id
|
||||
for column in gen_table_columns:
|
||||
@@ -470,9 +436,7 @@ class GenTableService:
|
||||
|
||||
forbidden = (Delete, Drop, Insert, TruncateTable, Update)
|
||||
if any(isinstance(s, forbidden) for s in sql_statements):
|
||||
raise CustomException(
|
||||
msg="sql语句包含禁止的关键操作(DROP/DELETE/INSERT/UPDATE/TRUNCATE)"
|
||||
)
|
||||
raise CustomException(msg="sql语句包含禁止的关键操作(DROP/DELETE/INSERT/UPDATE/TRUNCATE)")
|
||||
|
||||
# 获取要创建的表名
|
||||
table_names = []
|
||||
@@ -495,9 +459,7 @@ class GenTableService:
|
||||
# 检查代码生成模块中是否已导入该表
|
||||
existing_table = await gen_table_crud.get_gen_table_by_name(table_name)
|
||||
if existing_table:
|
||||
raise CustomException(
|
||||
msg=f"表 {table_name} 已在代码生成模块中存在,请检查并修改表名后重试"
|
||||
)
|
||||
raise CustomException(msg=f"表 {table_name} 已在代码生成模块中存在,请检查并修改表名后重试")
|
||||
|
||||
# 表不存在,执行SQL语句创建表
|
||||
for sql_statement in sql_statements:
|
||||
@@ -510,27 +472,15 @@ class GenTableService:
|
||||
# ALTER 仅允许添加外键约束,避免任意 ALTER 带来的破坏性
|
||||
if isinstance(sql_statement, Alter):
|
||||
upper = exc_sql.upper()
|
||||
allow = (
|
||||
"ALTER TABLE" in upper
|
||||
and "ADD" in upper
|
||||
and "CONSTRAINT" in upper
|
||||
and "FOREIGN KEY" in upper
|
||||
and "DROP" not in upper
|
||||
and "RENAME" not in upper
|
||||
and "SET " not in upper
|
||||
)
|
||||
allow = "ALTER TABLE" in upper and "ADD" in upper and "CONSTRAINT" in upper and "FOREIGN KEY" in upper and "DROP" not in upper and "RENAME" not in upper and "SET " not in upper
|
||||
if not allow:
|
||||
raise CustomException(
|
||||
msg="仅允许 ALTER TABLE ADD CONSTRAINT ... FOREIGN KEY ...(拒绝其它 ALTER)"
|
||||
)
|
||||
raise CustomException(msg="仅允许 ALTER TABLE ADD CONSTRAINT ... FOREIGN KEY ...(拒绝其它 ALTER)")
|
||||
if not await gen_table_crud.execute_sql(exc_sql):
|
||||
raise CustomException(msg=f"执行SQL语句 {exc_sql} 失败,请检查数据库")
|
||||
|
||||
# 建表成功后自动导入到代码生成模块
|
||||
if table_names:
|
||||
gen_table_list = await cls.get_gen_db_table_list_by_name_service(
|
||||
auth, table_names
|
||||
)
|
||||
gen_table_list = await cls.get_gen_db_table_list_by_name_service(auth, table_names)
|
||||
if gen_table_list:
|
||||
await cls.import_gen_table_service(auth, gen_table_list)
|
||||
|
||||
@@ -541,9 +491,7 @@ class GenTableService:
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def update_gen_table_service(
|
||||
cls, auth: AuthSchema, data: GenTableSchema, table_id: int
|
||||
) -> GenTableOutSchema:
|
||||
async def update_gen_table_service(cls, auth: AuthSchema, data: GenTableSchema, table_id: int) -> GenTableOutSchema:
|
||||
"""编辑业务表信息。
|
||||
|
||||
参数:
|
||||
@@ -566,40 +514,26 @@ class GenTableService:
|
||||
raise CustomException(msg="更新业务表信息失败")
|
||||
|
||||
if data.columns is not None:
|
||||
db_columns = await GenTableColumnCRUD(auth).list_gen_table_column_crud(
|
||||
search={"table_id": table_id}
|
||||
)
|
||||
db_columns = await GenTableColumnCRUD(auth).list_gen_table_column_crud(search={"table_id": table_id})
|
||||
db_column_map = {c.column_name: c for c in db_columns if c.column_name}
|
||||
submitted_names = {
|
||||
c.column_name
|
||||
for c in data.columns
|
||||
if hasattr(c, "column_name") and c.column_name
|
||||
}
|
||||
submitted_names = {c.column_name for c in data.columns if hasattr(c, "column_name") and c.column_name}
|
||||
|
||||
for gen_table_column in data.columns:
|
||||
col_id = getattr(gen_table_column, "id", None)
|
||||
col_name = getattr(gen_table_column, "column_name", None)
|
||||
if col_id and col_name and col_name in db_column_map:
|
||||
# 只更新前端实际修改的字段(利用 Pydantic model_fields_set)
|
||||
update_data = gen_table_column.model_dump(
|
||||
exclude_unset=True, exclude={"id", "super_column"}
|
||||
)
|
||||
update_data = gen_table_column.model_dump(exclude_unset=True, exclude={"id", "super_column"})
|
||||
if update_data:
|
||||
await GenTableColumnCRUD(auth).update(
|
||||
id=col_id, data=update_data
|
||||
)
|
||||
await GenTableColumnCRUD(auth).update(id=col_id, data=update_data)
|
||||
else:
|
||||
# 新增列:前端新增但库中无对应记录
|
||||
column_schema = GenTableColumnSchema(
|
||||
table_id=table_id,
|
||||
**gen_table_column.model_dump(
|
||||
exclude={"id", "super_column"}
|
||||
),
|
||||
**gen_table_column.model_dump(exclude={"id", "super_column"}),
|
||||
)
|
||||
GenUtils.init_column_field(column_schema, gen_table_info)
|
||||
await GenTableColumnCRUD(auth).create_gen_table_column_crud(
|
||||
column_schema
|
||||
)
|
||||
await GenTableColumnCRUD(auth).create_gen_table_column_crud(column_schema)
|
||||
|
||||
# 删除前端已移除的列
|
||||
for db_name, db_col in db_column_map.items():
|
||||
@@ -646,9 +580,7 @@ class GenTableService:
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def get_gen_table_by_id_service(
|
||||
cls, auth: AuthSchema, table_id: int
|
||||
) -> GenTableOutSchema:
|
||||
async def get_gen_table_by_id_service(cls, auth: AuthSchema, table_id: int) -> GenTableOutSchema:
|
||||
"""获取需要生成代码的业务表详细信息。
|
||||
|
||||
参数:
|
||||
@@ -712,9 +644,7 @@ class GenTableService:
|
||||
# 预览回显的路径/包名规则必须与「写入本地」一致:
|
||||
# - 选择上级目录:继承上级目录所属 module_xxx
|
||||
# - 未选上级目录:使用表单包名(并补齐 module_ 前缀)
|
||||
gen_table.package_name = await cls._effective_package_name(
|
||||
auth, gen_table.parent_menu_id, gen_table.package_name
|
||||
)
|
||||
gen_table.package_name = await cls._effective_package_name(auth, gen_table.parent_menu_id, gen_table.package_name)
|
||||
# 子表与主表同分系统/同模块
|
||||
if gen_table.sub and gen_table.sub_table:
|
||||
gen_table.sub_table.package_name = gen_table.package_name
|
||||
@@ -781,9 +711,7 @@ class GenTableService:
|
||||
from app.utils.common_util import CamelCaseUtil
|
||||
|
||||
# 按“上级目录”规则矫正最终包名(分系统根)
|
||||
gen_table_schema.package_name = await cls._effective_package_name(
|
||||
auth, gen_table_schema.parent_menu_id, gen_table_schema.package_name
|
||||
)
|
||||
gen_table_schema.package_name = await cls._effective_package_name(auth, gen_table_schema.parent_menu_id, gen_table_schema.package_name)
|
||||
# 统一权限前缀(对齐 module_example/demo):
|
||||
# - module_xxx:module_name(操作在按钮/模板中追加 :query/:create...)
|
||||
pn = (gen_table_schema.package_name or "").strip()
|
||||
@@ -797,9 +725,7 @@ class GenTableService:
|
||||
raise CustomException(msg="包名不能为空")
|
||||
|
||||
# 1. 先写代码文件(风险最高,放最前,失败不产生菜单孤儿数据)
|
||||
async def _write_templates(
|
||||
templates: list[str], ctx: dict[str, Any], table_schema: GenTableOutSchema
|
||||
) -> None:
|
||||
async def _write_templates(templates: list[str], ctx: dict[str, Any], table_schema: GenTableOutSchema) -> None:
|
||||
for template in templates:
|
||||
try:
|
||||
render_content = await env.get_template(template).render_async(**ctx)
|
||||
@@ -820,20 +746,14 @@ class GenTableService:
|
||||
init_path = d.joinpath("__init__.py")
|
||||
if not init_path.exists():
|
||||
os.makedirs(str(d), exist_ok=True)
|
||||
await anyio.Path(str(init_path)).write_text(
|
||||
"# -*- coding: utf-8 -*-", encoding="utf-8"
|
||||
)
|
||||
await anyio.Path(str(init_path)).write_text("# -*- coding: utf-8 -*-", encoding="utf-8")
|
||||
except Exception as e:
|
||||
raise CustomException(
|
||||
msg=f"渲染模板失败,表名:{table_schema.table_name},详细错误信息:{e!s}"
|
||||
)
|
||||
raise CustomException(msg=f"渲染模板失败,表名:{table_schema.table_name},详细错误信息:{e!s}")
|
||||
|
||||
await _write_templates(render_info[0], render_info[2], gen_table_schema)
|
||||
if gen_table_schema.sub and gen_table_schema.sub_table:
|
||||
gen_table_schema.sub_table.package_name = gen_table_schema.package_name
|
||||
sub_ctx = Jinja2TemplateUtil.prepare_sub_render_context(
|
||||
gen_table_schema, gen_table_schema.sub_table
|
||||
)
|
||||
sub_ctx = Jinja2TemplateUtil.prepare_sub_render_context(gen_table_schema, gen_table_schema.sub_table)
|
||||
sub_templates = Jinja2TemplateUtil.get_sub_table_template_list()
|
||||
await _write_templates(sub_templates, sub_ctx, gen_table_schema.sub_table)
|
||||
|
||||
@@ -856,9 +776,7 @@ class GenTableService:
|
||||
parent_id=dir_menu_id,
|
||||
)
|
||||
if existing_func_menu:
|
||||
raise CustomException(
|
||||
msg=f"该模块目录下功能菜单「{gen_table_schema.function_name}」已存在,不能重复创建"
|
||||
)
|
||||
raise CustomException(msg=f"该模块目录下功能菜单「{gen_table_schema.function_name}」已存在,不能重复创建")
|
||||
route_seg = cls._menu_route_first_segment(
|
||||
gen_table_schema.parent_menu_id,
|
||||
gen_table_schema.package_name or "",
|
||||
@@ -1005,24 +923,16 @@ class GenTableService:
|
||||
env = Jinja2TemplateUtil.get_env()
|
||||
render_info = await cls.__get_gen_render_info(auth, table_name)
|
||||
gen_tbl = render_info[3]
|
||||
for template_file, output_file in zip(
|
||||
render_info[0], render_info[1], strict=False
|
||||
):
|
||||
render_content = await env.get_template(template_file).render_async(
|
||||
**render_info[2]
|
||||
)
|
||||
for template_file, output_file in zip(render_info[0], render_info[1], strict=False):
|
||||
render_content = await env.get_template(template_file).render_async(**render_info[2])
|
||||
zip_file.writestr(output_file, render_content)
|
||||
file_count += 1
|
||||
if gen_tbl.sub and gen_tbl.sub_table:
|
||||
sub_ctx = Jinja2TemplateUtil.prepare_sub_render_context(
|
||||
gen_tbl, gen_tbl.sub_table
|
||||
)
|
||||
sub_ctx = Jinja2TemplateUtil.prepare_sub_render_context(gen_tbl, gen_tbl.sub_table)
|
||||
sub_tbl = gen_tbl.sub_table
|
||||
sub_template_list = Jinja2TemplateUtil.get_sub_table_template_list()
|
||||
for template_file in sub_template_list:
|
||||
render_content = await env.get_template(template_file).render_async(
|
||||
**sub_ctx
|
||||
)
|
||||
render_content = await env.get_template(template_file).render_async(**sub_ctx)
|
||||
out_path = Jinja2TemplateUtil.get_file_name(template_file, sub_tbl)
|
||||
zip_file.writestr(out_path, render_content)
|
||||
file_count += 1
|
||||
@@ -1034,16 +944,12 @@ class GenTableService:
|
||||
zip_data = zip_buffer.getvalue()
|
||||
zip_buffer.close()
|
||||
if file_count == 0:
|
||||
raise CustomException(
|
||||
msg="未能生成任何代码文件:请检查所选表是否存在于代码生成配置中,或主子表、字段配置是否正确"
|
||||
)
|
||||
raise CustomException(msg="未能生成任何代码文件:请检查所选表是否存在于代码生成配置中,或主子表、字段配置是否正确")
|
||||
return zip_data, failed_tables
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def sync_db_service(
|
||||
cls, auth: AuthSchema, table_name: str, _sync_sub: bool = True
|
||||
) -> None:
|
||||
async def sync_db_service(cls, auth: AuthSchema, table_name: str, _sync_sub: bool = True) -> None:
|
||||
"""
|
||||
同步数据库表结构到业务表。
|
||||
|
||||
@@ -1066,9 +972,7 @@ class GenTableService:
|
||||
table_columns = table.columns or []
|
||||
table_column_map = {column.column_name: column for column in table_columns}
|
||||
# 确保db_table_columns始终是列表类型,避免None值
|
||||
db_table_columns = (
|
||||
await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name) or []
|
||||
)
|
||||
db_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name) or []
|
||||
db_table_columns = [col for col in db_table_columns if col is not None]
|
||||
db_table_column_names = [column.column_name for column in db_table_columns]
|
||||
try:
|
||||
@@ -1091,9 +995,7 @@ class GenTableService:
|
||||
prev_column = table_column_map[column.column_name]
|
||||
if getattr(prev_column, "id", None):
|
||||
column.id = prev_column.id
|
||||
prev_dump = (
|
||||
prev_column.model_dump() if hasattr(prev_column, "model_dump") else {}
|
||||
)
|
||||
prev_dump = prev_column.model_dump() if hasattr(prev_column, "model_dump") else {}
|
||||
for k in preserve_keys:
|
||||
if k in prev_dump and prev_dump.get(k) not in (None, ""):
|
||||
setattr(column, k, prev_dump.get(k))
|
||||
@@ -1109,40 +1011,26 @@ class GenTableService:
|
||||
column.is_query = False
|
||||
column.query_type = None
|
||||
# is_nullable:主键列以 DB 为准,其余保留用户设置
|
||||
if not bool(getattr(column, "is_pk", False)) and hasattr(
|
||||
prev_column, "is_nullable"
|
||||
):
|
||||
if not bool(getattr(column, "is_pk", False)) and hasattr(prev_column, "is_nullable"):
|
||||
column.is_nullable = prev_column.is_nullable
|
||||
|
||||
# 转换为 GenTableColumnSchema,排除 super_column 等输出专用字段
|
||||
column_data = GenTableColumnSchema(
|
||||
**column.model_dump(exclude={"super_column"})
|
||||
)
|
||||
column_data = GenTableColumnSchema(**column.model_dump(exclude={"super_column"}))
|
||||
if hasattr(column, "id") and column.id:
|
||||
await GenTableColumnCRUD(auth).update_gen_table_column_crud(
|
||||
column.id, column_data
|
||||
)
|
||||
await GenTableColumnCRUD(auth).update_gen_table_column_crud(column.id, column_data)
|
||||
else:
|
||||
await GenTableColumnCRUD(auth).create_gen_table_column_crud(column_data)
|
||||
else:
|
||||
# 设置table_id以确保新字段能正确关联到表
|
||||
column.table_id = table.id
|
||||
# 转换为 GenTableColumnSchema,排除 super_column 等输出专用字段
|
||||
column_data = GenTableColumnSchema(
|
||||
**column.model_dump(exclude={"super_column"})
|
||||
)
|
||||
column_data = GenTableColumnSchema(**column.model_dump(exclude={"super_column"}))
|
||||
await GenTableColumnCRUD(auth).create_gen_table_column_crud(column_data)
|
||||
del_columns = [
|
||||
column
|
||||
for column in table_columns
|
||||
if column.column_name not in db_table_column_names
|
||||
]
|
||||
del_columns = [column for column in table_columns if column.column_name not in db_table_column_names]
|
||||
if del_columns:
|
||||
for column in del_columns:
|
||||
if hasattr(column, "id") and column.id:
|
||||
await GenTableColumnCRUD(auth).delete_gen_table_column_by_column_id_crud([
|
||||
column.id
|
||||
])
|
||||
await GenTableColumnCRUD(auth).delete_gen_table_column_by_column_id_crud([column.id])
|
||||
|
||||
# 主子表:若子表也已导入生成器,则一并同步子表配置
|
||||
sn = (table.sub_table_name or "").strip()
|
||||
@@ -1193,9 +1081,7 @@ class GenTableService:
|
||||
|
||||
# 1) 若子表已作为 gen_table 导入,则使用其 columns 配置(可控、可复用)
|
||||
try:
|
||||
sub_cfg_model = await GenTableCRUD(auth).get_gen_table_by_name(
|
||||
sub_name_raw, preload=["columns"]
|
||||
)
|
||||
sub_cfg_model = await GenTableCRUD(auth).get_gen_table_by_name(sub_name_raw, preload=["columns"])
|
||||
except Exception:
|
||||
sub_cfg_model = None
|
||||
if sub_cfg_model:
|
||||
@@ -1206,24 +1092,16 @@ class GenTableService:
|
||||
if fk_raw not in fk_names:
|
||||
gen_table.sub = False
|
||||
gen_table.sub_table = None
|
||||
gen_table.master_sub_hint = (
|
||||
f"子表「{sub_name_raw}」已导入生成器,但其字段配置中不存在外键列「{fk_raw}」。"
|
||||
"请先在子表的字段配置中同步/保存后再生成。"
|
||||
)
|
||||
gen_table.master_sub_hint = f"子表「{sub_name_raw}」已导入生成器,但其字段配置中不存在外键列「{fk_raw}」。请先在子表的字段配置中同步/保存后再生成。"
|
||||
return
|
||||
gen_table.sub = True
|
||||
gen_table.sub_table = sub_cfg
|
||||
gen_table.master_sub_hint = (
|
||||
"主子表已启用:子表字段来自「已导入的子表配置」(更可控、可复用)。"
|
||||
"如需调整子表字段,请在列表中打开该子表进行配置。"
|
||||
)
|
||||
gen_table.master_sub_hint = "主子表已启用:子表字段来自「已导入的子表配置」(更可控、可复用)。如需调整子表字段,请在列表中打开该子表进行配置。"
|
||||
return
|
||||
|
||||
# 2) 回退:仅从 DB 读取结构(只读,无法配置子表字段)
|
||||
try:
|
||||
gen_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(
|
||||
sub_name_raw
|
||||
)
|
||||
gen_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(sub_name_raw)
|
||||
except Exception as e:
|
||||
logger.warning(f"获取子表 {sub_name_raw} 字段失败: {e!s}")
|
||||
gen_table.sub = False
|
||||
@@ -1233,35 +1111,33 @@ class GenTableService:
|
||||
if not gen_table_columns:
|
||||
gen_table.sub = False
|
||||
gen_table.sub_table = None
|
||||
gen_table.master_sub_hint = (
|
||||
f"当前数据库中不存在表「{sub_name_raw}」或该表无列,请先建表再配置主子表"
|
||||
)
|
||||
gen_table.master_sub_hint = f"当前数据库中不存在表「{sub_name_raw}」或该表无列,请先建表再配置主子表"
|
||||
return
|
||||
fk_names = {c.column_name for c in gen_table_columns if c.column_name}
|
||||
if fk_raw not in fk_names:
|
||||
gen_table.sub = False
|
||||
gen_table.sub_table = None
|
||||
gen_table.master_sub_hint = (
|
||||
f"子表「{sub_name_raw}」中不存在名为「{fk_raw}」的列,请核对外键列名"
|
||||
)
|
||||
gen_table.master_sub_hint = f"子表「{sub_name_raw}」中不存在名为「{fk_raw}」的列,请核对外键列名"
|
||||
return
|
||||
table_comment = await GenTableCRUD(auth).get_db_table_comment(sub_name_raw)
|
||||
sub = GenTableOutSchema.model_validate({
|
||||
"id": -1,
|
||||
"table_name": sub_name_raw,
|
||||
"table_comment": table_comment or None,
|
||||
"class_name": GenUtils.convert_class_name(sub_name_raw),
|
||||
"package_name": gen_table.package_name,
|
||||
"module_name": sub_name_raw,
|
||||
"business_name": sub_name_raw,
|
||||
"function_name": re.sub(r"(?:表|测试)", "", table_comment or "") or sub_name_raw,
|
||||
"sub_table_name": None,
|
||||
"sub_table_fk_name": None,
|
||||
"parent_menu_id": gen_table.parent_menu_id,
|
||||
"columns": [],
|
||||
"sub": False,
|
||||
"sub_table": None,
|
||||
})
|
||||
sub = GenTableOutSchema.model_validate(
|
||||
{
|
||||
"id": -1,
|
||||
"table_name": sub_name_raw,
|
||||
"table_comment": table_comment or None,
|
||||
"class_name": GenUtils.convert_class_name(sub_name_raw),
|
||||
"package_name": gen_table.package_name,
|
||||
"module_name": sub_name_raw,
|
||||
"business_name": sub_name_raw,
|
||||
"function_name": re.sub(r"(?:表|测试)", "", table_comment or "") or sub_name_raw,
|
||||
"sub_table_name": None,
|
||||
"sub_table_fk_name": None,
|
||||
"parent_menu_id": gen_table.parent_menu_id,
|
||||
"columns": [],
|
||||
"sub": False,
|
||||
"sub_table": None,
|
||||
}
|
||||
)
|
||||
for column in gen_table_columns:
|
||||
col_dump = column.model_dump()
|
||||
col_dump["table_id"] = -1
|
||||
@@ -1273,10 +1149,7 @@ class GenTableService:
|
||||
await cls.set_pk_column(sub)
|
||||
gen_table.sub = True
|
||||
gen_table.sub_table = sub
|
||||
gen_table.master_sub_hint = (
|
||||
"主子表已启用:当前子表仅从数据库结构读取(只读)。"
|
||||
f"若想可配置子表字段,请先在「导入」中把子表「{sub_name_raw}」也导入生成器。"
|
||||
)
|
||||
gen_table.master_sub_hint = f"主子表已启用:当前子表仅从数据库结构读取(只读)。若想可配置子表字段,请先在「导入」中把子表「{sub_name_raw}」也导入生成器。"
|
||||
|
||||
@classmethod
|
||||
def _sync_preview_diff(
|
||||
@@ -1401,14 +1274,9 @@ class GenTableService:
|
||||
if not sn and not fk:
|
||||
return
|
||||
if not sn or not fk:
|
||||
raise CustomException(
|
||||
msg=gen_table.master_sub_hint or "子表表名与子表外键列须同时填写或同时留空"
|
||||
)
|
||||
raise CustomException(msg=gen_table.master_sub_hint or "子表表名与子表外键列须同时填写或同时留空")
|
||||
if not gen_table.sub_table:
|
||||
raise CustomException(
|
||||
msg=gen_table.master_sub_hint
|
||||
or "无法生成主子表代码:请确认子表已在当前数据库中存在,且外键列名正确"
|
||||
)
|
||||
raise CustomException(msg=gen_table.master_sub_hint or "无法生成主子表代码:请确认子表已在当前数据库中存在,且外键列名正确")
|
||||
|
||||
@classmethod
|
||||
async def set_pk_column(cls, gen_table: GenTableOutSchema) -> None:
|
||||
@@ -1452,17 +1320,13 @@ class GenTableService:
|
||||
raise CustomException(msg=f"业务表 {table_name} 不存在")
|
||||
gen_table = GenTableOutSchema.model_validate(gen_table_model)
|
||||
# 生成代码时按“上级目录”规则矫正最终包名(不落库,仅影响本次生成/预览/下载/写入)
|
||||
gen_table.package_name = await cls._effective_package_name(
|
||||
auth, gen_table.parent_menu_id, gen_table.package_name
|
||||
)
|
||||
gen_table.package_name = await cls._effective_package_name(auth, gen_table.parent_menu_id, gen_table.package_name)
|
||||
await cls.set_pk_column(gen_table)
|
||||
await cls.hydrate_sub_table(auth, gen_table)
|
||||
cls._assert_master_sub_config_valid(gen_table)
|
||||
context = Jinja2TemplateUtil.prepare_context(gen_table)
|
||||
template_list = Jinja2TemplateUtil.get_template_list()
|
||||
output_files = [
|
||||
Jinja2TemplateUtil.get_file_name(template, gen_table) for template in template_list
|
||||
]
|
||||
output_files = [Jinja2TemplateUtil.get_file_name(template, gen_table) for template in template_list]
|
||||
return [template_list, output_files, context, gen_table]
|
||||
|
||||
|
||||
@@ -1471,9 +1335,7 @@ class GenTableColumnService:
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def get_gen_table_column_list_by_table_id_service(
|
||||
cls, auth: AuthSchema, table_id: int
|
||||
) -> list[dict[str, Any]]:
|
||||
async def get_gen_table_column_list_by_table_id_service(cls, auth: AuthSchema, table_id: int) -> list[dict[str, Any]]:
|
||||
"""获取业务表字段列表信息(输出模型)。
|
||||
|
||||
参数:
|
||||
@@ -1483,11 +1345,6 @@ class GenTableColumnService:
|
||||
返回:
|
||||
- list[dict[str, Any]]: 业务表字段列表,每个元素为字段详细信息字典。
|
||||
"""
|
||||
gen_table_column_list_result = await GenTableColumnCRUD(auth).list_gen_table_column_crud({
|
||||
"table_id": table_id
|
||||
})
|
||||
result = [
|
||||
GenTableColumnOutSchema.model_validate(gen_table_column)
|
||||
for gen_table_column in gen_table_column_list_result
|
||||
]
|
||||
gen_table_column_list_result = await GenTableColumnCRUD(auth).list_gen_table_column_crud({"table_id": table_id})
|
||||
result = [GenTableColumnOutSchema.model_validate(gen_table_column) for gen_table_column in gen_table_column_list_result]
|
||||
return result
|
||||
|
||||
@@ -90,9 +90,7 @@ class GenUtils:
|
||||
column.python_type = "list"
|
||||
else:
|
||||
# 只有当python_type为None时才设置默认类型
|
||||
column.python_type = StringUtil.get_mapping_value_by_key_ignore_case(
|
||||
GenConstant.DB_TO_PYTHON, data_type
|
||||
)
|
||||
column.python_type = StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_PYTHON, data_type)
|
||||
|
||||
if column.column_length is None:
|
||||
column.column_length = ""
|
||||
@@ -118,48 +116,29 @@ class GenUtils:
|
||||
column.html_type = GenConstant.HTML_DATETIME
|
||||
elif cls.arrays_contains(GenConstant.COLUMNTYPE_NUMBER, data_type):
|
||||
column.html_type = GenConstant.HTML_INPUT
|
||||
elif cls.arrays_contains(GenConstant.COLUMNTYPE_STR, data_type) or cls.arrays_contains(
|
||||
GenConstant.COLUMNTYPE_TEXT, data_type
|
||||
):
|
||||
elif cls.arrays_contains(GenConstant.COLUMNTYPE_STR, data_type) or cls.arrays_contains(GenConstant.COLUMNTYPE_TEXT, data_type):
|
||||
# 字符串长度超过500设置为文本域
|
||||
column_length = cls.get_column_length(column.column_type or "")
|
||||
column.html_type = (
|
||||
GenConstant.HTML_TEXTAREA
|
||||
if column_length >= 500
|
||||
or cls.arrays_contains(GenConstant.COLUMNTYPE_TEXT, data_type)
|
||||
else GenConstant.HTML_INPUT
|
||||
)
|
||||
column.html_type = GenConstant.HTML_TEXTAREA if column_length >= 500 or cls.arrays_contains(GenConstant.COLUMNTYPE_TEXT, data_type) else GenConstant.HTML_INPUT
|
||||
else:
|
||||
column.html_type = GenConstant.HTML_INPUT
|
||||
|
||||
# 默认新增字段:非主键且不在“新增不展示”黑名单中
|
||||
# 说明:schema 默认值可能为 True/False;仅当调用方未显式配置时才做推断
|
||||
if column.is_insert is None:
|
||||
column.is_insert = bool(
|
||||
(not column.is_pk)
|
||||
and (not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_ADD_SHOW, column_name))
|
||||
)
|
||||
column.is_insert = bool((not column.is_pk) and (not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_ADD_SHOW, column_name)))
|
||||
|
||||
# 默认编辑字段:非主键且不在“不编辑”黑名单中
|
||||
if column.is_edit is None:
|
||||
column.is_edit = bool(
|
||||
(not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_EDIT, column_name))
|
||||
and (not column.is_pk)
|
||||
)
|
||||
column.is_edit = bool((not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_EDIT, column_name)) and (not column.is_pk))
|
||||
|
||||
# 默认列表字段:非主键且不在“不列表显示”黑名单中
|
||||
if column.is_list is None:
|
||||
column.is_list = bool(
|
||||
(not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_LIST, column_name))
|
||||
and (not column.is_pk)
|
||||
)
|
||||
column.is_list = bool((not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_LIST, column_name)) and (not column.is_pk))
|
||||
|
||||
# 默认查询字段:非主键且不在“不查询”黑名单中
|
||||
if column.is_query is None:
|
||||
column.is_query = bool(
|
||||
(not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_QUERY, column_name))
|
||||
and (not column.is_pk)
|
||||
)
|
||||
column.is_query = bool((not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_QUERY, column_name)) and (not column.is_pk))
|
||||
|
||||
# 查询类型:仅当开启查询且 query_type 未显式配置时推断
|
||||
if column.is_query:
|
||||
|
||||
@@ -71,12 +71,14 @@ class Jinja2TemplateUtil:
|
||||
keep_trailing_newline=True, # 保留行尾换行符
|
||||
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,
|
||||
})
|
||||
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,
|
||||
}
|
||||
)
|
||||
return cls._env
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"初始化Jinja2模板引擎失败: {e}")
|
||||
@@ -178,9 +180,7 @@ class Jinja2TemplateUtil:
|
||||
api_route_prefix = cls.get_api_route_prefix(package_name)
|
||||
|
||||
_cols = gen_table.columns or []
|
||||
table_column_names = frozenset(
|
||||
c.column_name for c in _cols if getattr(c, "column_name", None)
|
||||
)
|
||||
table_column_names = frozenset(c.column_name for c in _cols if getattr(c, "column_name", None))
|
||||
|
||||
sub_class_name = ""
|
||||
sub_model_class_name = ""
|
||||
@@ -188,9 +188,7 @@ class Jinja2TemplateUtil:
|
||||
parent_rel_name = ""
|
||||
if gen_table.sub and gen_table.sub_table:
|
||||
st = gen_table.sub_table
|
||||
scn = (
|
||||
st.class_name or GenUtils.convert_class_name(gen_table.sub_table_name or "")
|
||||
).strip()
|
||||
scn = (st.class_name or GenUtils.convert_class_name(gen_table.sub_table_name or "")).strip()
|
||||
sub_class_name = scn
|
||||
sub_model_class_name = f"{scn}Model"
|
||||
sub_rel_list_name = f"{SnakeCaseUtil.camel_to_snake(scn)}_list"
|
||||
@@ -199,9 +197,7 @@ class Jinja2TemplateUtil:
|
||||
context = {
|
||||
"table_name": gen_table.table_name or "",
|
||||
"table_comment": gen_table.table_comment or "",
|
||||
"function_name": function_name
|
||||
if StringUtil.is_not_empty(function_name)
|
||||
else "【请填写功能名称】",
|
||||
"function_name": function_name if StringUtil.is_not_empty(function_name) else "【请填写功能名称】",
|
||||
"class_name": class_name,
|
||||
"module_name": module_name,
|
||||
"business_name": business_name,
|
||||
@@ -228,21 +224,15 @@ class Jinja2TemplateUtil:
|
||||
"is_sub_entity": False,
|
||||
"sub_class_name": sub_class_name,
|
||||
"sub_model_class_name": sub_model_class_name,
|
||||
"sub_module_name": (
|
||||
gen_table.sub_table.module_name if gen_table.sub and gen_table.sub_table else ""
|
||||
),
|
||||
"sub_module_name": (gen_table.sub_table.module_name if gen_table.sub and gen_table.sub_table else ""),
|
||||
"sub_rel_list_name": sub_rel_list_name,
|
||||
"parent_rel_name": parent_rel_name,
|
||||
"parent_list_rel_name": "",
|
||||
"parent_table_name": "",
|
||||
"parent_model_class_name": "",
|
||||
# 数据表实际主键列名(用于生成前端行键等;ModelMixin 仍默认带 id 字段)
|
||||
"pk_column_name": (gen_table.pk_column.column_name if gen_table.pk_column else None)
|
||||
or "id",
|
||||
"parent_pk_column_name": (
|
||||
gen_table.pk_column.column_name if gen_table.pk_column else None
|
||||
)
|
||||
or "id",
|
||||
"pk_column_name": (gen_table.pk_column.column_name if gen_table.pk_column else None) or "id",
|
||||
"parent_pk_column_name": (gen_table.pk_column.column_name if gen_table.pk_column else None) or "id",
|
||||
"sub_table_fk_name": "",
|
||||
}
|
||||
|
||||
@@ -271,9 +261,7 @@ class Jinja2TemplateUtil:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def prepare_sub_render_context(
|
||||
cls, parent: GenTableOutSchema, sub: GenTableOutSchema
|
||||
) -> dict[str, Any]:
|
||||
def prepare_sub_render_context(cls, parent: GenTableOutSchema, sub: GenTableOutSchema) -> dict[str, Any]:
|
||||
"""
|
||||
子表业务代码渲染上下文(与主表同模块、独立业务目录)。
|
||||
|
||||
@@ -290,9 +278,7 @@ class Jinja2TemplateUtil:
|
||||
ctx["parent_class_name"] = parent.class_name or ""
|
||||
ctx["parent_model_class_name"] = f"{parent.class_name}Model"
|
||||
ctx["parent_table_name"] = parent.table_name or ""
|
||||
ctx["parent_pk_column_name"] = (
|
||||
parent.pk_column.column_name if parent.pk_column else None
|
||||
) or "id"
|
||||
ctx["parent_pk_column_name"] = (parent.pk_column.column_name if parent.pk_column else None) or "id"
|
||||
ctx["parent_rel_name"] = SnakeCaseUtil.camel_to_snake(parent.class_name or "parent")
|
||||
ctx["parent_list_rel_name"] = f"{SnakeCaseUtil.camel_to_snake(scn)}_list"
|
||||
ctx["sub_table_fk_name"] = (parent.sub_table_fk_name or "").strip()
|
||||
@@ -473,9 +459,7 @@ class Jinja2TemplateUtil:
|
||||
return import_list
|
||||
|
||||
@classmethod
|
||||
def get_model_import_list(
|
||||
cls, gen_table: GenTableOutSchema, *, is_sub_entity: bool = False
|
||||
) -> list[str]:
|
||||
def get_model_import_list(cls, gen_table: GenTableOutSchema, *, is_sub_entity: bool = False) -> list[str]:
|
||||
"""
|
||||
获取 model 模板所需的 Python 导入语句列表(含合并后的 sqlalchemy 导入)。
|
||||
|
||||
@@ -497,9 +481,7 @@ class Jinja2TemplateUtil:
|
||||
data_type = cls.get_db_type(column.column_type)
|
||||
if data_type in GenConstant.COLUMNTYPE_GEOMETRY:
|
||||
import_list.add("from geoalchemy2 import Geometry")
|
||||
import_list.add(
|
||||
f"from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}"
|
||||
)
|
||||
import_list.add(f"from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}")
|
||||
# 处理datetime类型的导入
|
||||
if column.python_type and column.python_type in GenConstant.TYPE_DATE:
|
||||
if column.python_type == "datetime":
|
||||
@@ -513,19 +495,12 @@ class Jinja2TemplateUtil:
|
||||
import_list.add("from decimal import Decimal")
|
||||
if gen_table.sub or is_sub_entity:
|
||||
import_list.add("from sqlalchemy import ForeignKey")
|
||||
if (
|
||||
gen_table.sub
|
||||
and not is_sub_entity
|
||||
and gen_table.sub_table
|
||||
and gen_table.sub_table.columns
|
||||
):
|
||||
if gen_table.sub and not is_sub_entity and gen_table.sub_table and gen_table.sub_table.columns:
|
||||
sub_columns = gen_table.sub_table.columns or []
|
||||
for sub_column in sub_columns:
|
||||
if sub_column.column_type:
|
||||
data_type = cls.get_db_type(sub_column.column_type)
|
||||
import_list.add(
|
||||
f"from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}"
|
||||
)
|
||||
import_list.add(f"from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}")
|
||||
# 处理datetime类型的导入
|
||||
if sub_column.python_type and sub_column.python_type in GenConstant.TYPE_DATE:
|
||||
if sub_column.python_type == "datetime":
|
||||
@@ -760,9 +735,7 @@ class Jinja2TemplateUtil:
|
||||
return "Boolean"
|
||||
|
||||
# 首先尝试匹配完整类型(包括括号)
|
||||
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(
|
||||
GenConstant.DB_TO_SQLALCHEMY, column_type
|
||||
)
|
||||
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, column_type)
|
||||
|
||||
# 特殊处理PostgreSQL类型
|
||||
if settings.DATABASE_TYPE == "postgres":
|
||||
@@ -789,9 +762,7 @@ class Jinja2TemplateUtil:
|
||||
# 将 'character' 映射为 'char' 以匹配常量定义
|
||||
if col_type.lower() == "character":
|
||||
col_type = "char"
|
||||
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(
|
||||
GenConstant.DB_TO_SQLALCHEMY, col_type
|
||||
)
|
||||
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, col_type)
|
||||
# 如果是字符串类型且包含括号参数,保持原参数
|
||||
if sqlalchemy_type in ["String", "CHAR"]:
|
||||
sqlalchemy_type += "(" + column_type_list[1]
|
||||
@@ -804,9 +775,7 @@ class Jinja2TemplateUtil:
|
||||
# 将 'character' 映射为 'char' 以匹配常量定义
|
||||
if col_type.lower() == "character":
|
||||
col_type = "char"
|
||||
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(
|
||||
GenConstant.DB_TO_SQLALCHEMY, col_type
|
||||
)
|
||||
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, col_type)
|
||||
# 如果是字符串类型且没有指定长度,使用column_length或默认255
|
||||
if sqlalchemy_type in ["String", "CHAR"]:
|
||||
length = column_length if column_length and column_length.isdigit() else "255"
|
||||
|
||||
Reference in New Issue
Block a user