mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 12:52:26 +00:00
- 新增代码生成模块路由支持 - 重构 MCP 服务器API,支持增删改查接口和分页查询 - 实现 MCP 智能对话的流式响应和 WebSocket 通信 - 优化 MCP 数据模型,支持环境变量等配置项 - 改进 MCP CRUD 层,集成权限控制和统一操作方法 - 优化异常处理,提升接口错误信息准确性 - 修正依赖导入和数据校验,增强代码健壮性
197 lines
7.5 KiB
Python
197 lines
7.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
基础模型模块
|
|
提供跨数据库兼容的基础模型类和类型装饰器
|
|
"""
|
|
|
|
from datetime import datetime
|
|
import re
|
|
from typing import Literal, Optional, Dict, Any, Union
|
|
|
|
from sqlalchemy import Boolean, String, Integer, DateTime, ForeignKey, Text, BigInteger
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
from sqlalchemy.ext.asyncio import AsyncAttrs
|
|
from sqlalchemy.orm import relationship, DeclarativeBase, Mapped, declared_attr, mapped_column, MappedAsDataclass
|
|
from sqlalchemy.engine.row import Row
|
|
from sqlalchemy.orm.collections import InstrumentedList
|
|
|
|
|
|
class MappedBase(AsyncAttrs, DeclarativeBase):
|
|
"""
|
|
声明式基类
|
|
|
|
`AsyncAttrs <https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#sqlalchemy.ext.asyncio.AsyncAttrs>`__
|
|
|
|
`DeclarativeBase <https://docs.sqlalchemy.org/en/20/orm/declarative_config.html>`__
|
|
|
|
`mapped_column() <https://docs.sqlalchemy.org/en/20/orm/mapping_api.html#sqlalchemy.orm.mapped_column>`__
|
|
|
|
兼容 SQLite、MySQL 和 PostgreSQL
|
|
"""
|
|
|
|
__abstract__ = True
|
|
|
|
@declared_attr.directive
|
|
def __tablename__(cls) -> str:
|
|
"""生成表名"""
|
|
return cls.__name__.lower()
|
|
|
|
@declared_attr.directive
|
|
def __table_args__(cls) -> dict:
|
|
"""表配置"""
|
|
return {'comment': cls.__doc__ or ''}
|
|
|
|
|
|
class ModelMixin(MappedBase):
|
|
"""
|
|
基础模型混合类
|
|
"""
|
|
__abstract__ = True
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
|
|
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True, default=None, comment="备注/描述")
|
|
created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, default=datetime.now, comment='创建时间')
|
|
create_by: Mapped[Optional[str]] = mapped_column(String(64), default='', comment='创建者')
|
|
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment='更新时间')
|
|
update_by: Mapped[Optional[str]] = mapped_column(String(64), default='', comment='更新者')
|
|
del_flag: Mapped[str] = mapped_column(String(1), nullable=False, default='0', comment='删除标志(0代表存在 2代表删除)')
|
|
|
|
|
|
class CreatorMixin(ModelMixin):
|
|
"""
|
|
创建人混合类
|
|
"""
|
|
__abstract__ = True
|
|
|
|
creator_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, index=True, comment="创建人ID")
|
|
|
|
@declared_attr
|
|
def creator(cls) -> Mapped[Optional["UserModel"]]:
|
|
"""创建人关联关系(延迟加载,避免循环依赖)"""
|
|
return relationship(
|
|
"UserModel",
|
|
primaryjoin=f"{cls.__name__}.creator_id == UserModel.id",
|
|
lazy="selectin",
|
|
foreign_keys=[cls.creator_id],
|
|
viewonly=True,
|
|
uselist=False # 明确指定返回单个对象
|
|
)
|
|
|
|
|
|
class SqlalchemyUtil:
|
|
"""
|
|
sqlalchemy工具类
|
|
"""
|
|
|
|
@classmethod
|
|
def base_to_dict(
|
|
cls, obj: Union[MappedBase, Dict], transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case'
|
|
) -> dict[str, Any] | dict[Any, Any]:
|
|
"""
|
|
将sqlalchemy模型对象转换为字典
|
|
|
|
:param obj: sqlalchemy模型对象或普通字典
|
|
:param transform_case: 转换得到的结果形式,可选的有'no_case'(不转换)、'snake_to_camel'(下划线转小驼峰)、'camel_to_snake'(小驼峰转下划线),默认为'no_case'
|
|
:return: 字典结果
|
|
"""
|
|
if isinstance(obj, MappedBase):
|
|
base_dict = obj.__dict__.copy()
|
|
base_dict.pop('_sa_instance_state', None)
|
|
for name, value in base_dict.items():
|
|
if isinstance(value, InstrumentedList):
|
|
base_dict[name] = cls.serialize_result(value, 'snake_to_camel')
|
|
elif isinstance(obj, dict):
|
|
base_dict = obj.copy()
|
|
if transform_case == 'snake_to_camel':
|
|
return {CamelCaseUtil.snake_to_camel(k): v for k, v in base_dict.items()}
|
|
elif transform_case == 'camel_to_snake':
|
|
return {SnakeCaseUtil.camel_to_snake(k): v for k, v in base_dict.items()}
|
|
|
|
return base_dict
|
|
|
|
@classmethod
|
|
def serialize_result(
|
|
cls, result: Any, transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case'
|
|
) -> Any:
|
|
"""
|
|
将sqlalchemy查询结果序列化
|
|
|
|
:param result: sqlalchemy查询结果
|
|
:param transform_case: 转换得到的结果形式,可选的有'no_case'(不转换)、'snake_to_camel'(下划线转小驼峰)、'camel_to_snake'(小驼峰转下划线),默认为'no_case'
|
|
:return: 序列化结果
|
|
"""
|
|
if isinstance(result, (MappedBase, dict)):
|
|
return cls.base_to_dict(result, transform_case)
|
|
elif isinstance(result, list):
|
|
return [cls.serialize_result(row, transform_case) for row in result]
|
|
elif isinstance(result, Row):
|
|
if all([isinstance(row, MappedBase) for row in result]):
|
|
return [cls.base_to_dict(row, transform_case) for row in result]
|
|
elif any([isinstance(row, MappedBase) for row in result]):
|
|
return [cls.serialize_result(row, transform_case) for row in result]
|
|
else:
|
|
result_dict = result._asdict()
|
|
if transform_case == 'snake_to_camel':
|
|
return {CamelCaseUtil.snake_to_camel(k): v for k, v in result_dict.items()}
|
|
elif transform_case == 'camel_to_snake':
|
|
return {SnakeCaseUtil.camel_to_snake(k): v for k, v in result_dict.items()}
|
|
return result_dict
|
|
return result
|
|
|
|
|
|
class CamelCaseUtil:
|
|
"""
|
|
下划线形式(snake_case)转小驼峰形式(camelCase)工具方法
|
|
"""
|
|
|
|
@classmethod
|
|
def snake_to_camel(cls, snake_str: str) -> str:
|
|
"""
|
|
下划线形式字符串(snake_case)转换为小驼峰形式字符串(camelCase)
|
|
|
|
:param snake_str: 下划线形式字符串
|
|
:return: 小驼峰形式字符串
|
|
"""
|
|
# 分割字符串
|
|
words = snake_str.split('_')
|
|
# 小驼峰命名,第一个词首字母小写,其余词首字母大写
|
|
return words[0] + ''.join(word.capitalize() for word in words[1:])
|
|
|
|
@classmethod
|
|
def transform_result(cls, result: Any) -> Any:
|
|
"""
|
|
针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法
|
|
|
|
:param result: 输入数据
|
|
:return: 小驼峰形式结果
|
|
"""
|
|
return SqlalchemyUtil.serialize_result(result=result, transform_case='snake_to_camel')
|
|
|
|
|
|
class SnakeCaseUtil:
|
|
"""
|
|
小驼峰形式(camelCase)转下划线形式(snake_case)工具方法
|
|
"""
|
|
|
|
@classmethod
|
|
def camel_to_snake(cls, camel_str: str) -> str:
|
|
"""
|
|
小驼峰形式字符串(camelCase)转换为下划线形式字符串(snake_case)
|
|
|
|
:param camel_str: 小驼峰形式字符串
|
|
:return: 下划线形式字符串
|
|
"""
|
|
# 在大写字母前添加一个下划线,然后将整个字符串转为小写
|
|
words = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel_str)
|
|
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', words).lower()
|
|
|
|
@classmethod
|
|
def transform_result(cls, result: Any) -> Any:
|
|
"""
|
|
针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法
|
|
|
|
:param result: 输入数据
|
|
:return: 小驼峰形式结果
|
|
"""
|
|
return SqlalchemyUtil.serialize_result(result=result, transform_case='camel_to_snake')
|