From 2b282440995a6188f65d558ab88e75c529e23ae9 Mon Sep 17 00:00:00 2001 From: wuyao4 <121445068+wuyao4@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:17:50 +0800 Subject: [PATCH] Fix the plugin model object detection logic (#782) --- backend/alembic/env.py | 3 --- backend/app/__init__.py | 26 ++++++++------------------ backend/plugin/tools.py | 17 ++++++----------- backend/utils/import_parse.py | 25 ++++++++++++++++++++++++- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/backend/alembic/env.py b/backend/alembic/env.py index 4bdd3cce..5f3a45a4 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -3,7 +3,6 @@ # ruff: noqa: F403, F401, I001, RUF100 import asyncio import os -import sys from logging.config import fileConfig from alembic import context @@ -11,8 +10,6 @@ from sqlalchemy import pool from sqlalchemy.engine import Connection from sqlalchemy.ext.asyncio import async_engine_from_config -sys.path.append('../') - from backend.app import get_app_models from backend.common.model import MappedBase from backend.core import path_conf diff --git a/backend/app/__init__.py b/backend/app/__init__.py index 097c2e18..d0139d09 100644 --- a/backend/app/__init__.py +++ b/backend/app/__init__.py @@ -1,14 +1,12 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import inspect import os.path -from backend.common.log import log from backend.core.path_conf import BASE_PATH -from backend.utils.import_parse import import_module_cached +from backend.utils.import_parse import get_model_object -def get_app_models(): +def get_app_models() -> list[type]: """获取 app 所有模型类""" app_path = os.path.join(BASE_PATH, 'app') list_dirs = os.listdir(app_path) @@ -19,23 +17,15 @@ def get_app_models(): if os.path.isdir(os.path.join(app_path, d)) and d != '__pycache__': apps.append(d) - classes = [] + objs = [] for app in apps: - try: - module_path = f'backend.app.{app}.model' - module = import_module_cached(module_path) - except ModuleNotFoundError as e: - log.warning(f'应用 {app} 中不包含 model 相关配置: {e}') - continue - except Exception as e: - raise e + module_path = f'backend.app.{app}.model' + obj = get_model_object(module_path) + if obj: + objs.append(obj) - for name, obj in inspect.getmembers(module): - if inspect.isclass(obj): - classes.append(obj) - - return classes + return objs # import all app models for auto create db tables diff --git a/backend/plugin/tools.py b/backend/plugin/tools.py index 88802eca..3a285dfe 100644 --- a/backend/plugin/tools.py +++ b/backend/plugin/tools.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import inspect import json import os import subprocess @@ -24,7 +23,7 @@ from backend.core.conf import settings from backend.core.path_conf import PLUGIN_DIR from backend.database.redis import RedisCli, redis_client from backend.utils._await import run_await -from backend.utils.import_parse import import_module_cached +from backend.utils.import_parse import get_model_object, import_module_cached class PluginConfigError(Exception): @@ -60,19 +59,15 @@ def get_plugins() -> list[str]: def get_plugin_models() -> list[type]: """获取插件所有模型类""" - classes = [] + objs = [] for plugin in get_plugins(): - # 导入插件的模型模块 module_path = f'backend.plugin.{plugin}.model' - module = import_module_cached(module_path) + obj = get_model_object(module_path) + if obj: + objs.append(obj) - # 获取模块中的所有类 - for name, obj in inspect.getmembers(module): - if inspect.isclass(obj): - classes.append(obj) - - return classes + return objs async def get_plugin_sql(plugin: str, db_type: DataBaseType, pk_type: PrimaryKeyType) -> str | None: diff --git a/backend/utils/import_parse.py b/backend/utils/import_parse.py index 55a3e26e..2bf0bb58 100644 --- a/backend/utils/import_parse.py +++ b/backend/utils/import_parse.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import importlib +import inspect from functools import lru_cache from typing import Any, Type, TypeVar @@ -33,6 +34,28 @@ def dynamic_import_data_model(module_path: str) -> Type[T]: module_path, class_name = module_path.rsplit('.', 1) module = import_module_cached(module_path) return getattr(module, class_name) - except (ImportError, AttributeError) as e: + except Exception as e: log.error(f'动态导入数据模型失败:{e}') raise errors.ServerError(msg='数据模型列动态解析失败,请联系系统超级管理员') + + +def get_model_object(module_path: str) -> type | None: + """ + 获取模型对象 + + :param module_path: 模块路径 + :return: + """ + try: + module = import_module_cached(module_path) + except ModuleNotFoundError: + log.warning(f'模块 {module_path} 中不包含模型对象') + return None + except Exception as e: + raise RuntimeError(f'获取模块 {module_path} 模型对象失败:{e}') + + for name, obj in inspect.getmembers(module): + if inspect.isclass(obj): + return obj + + return None