如何正确实现多文件共用同一个SQLAlchemy declarative_base()
报错原因
报错本质是Python导入路径匹配规则导致的:
- 单独运行
models目录下的building.py/user.py时,Python的工作目录是models/,写from base import Base可以直接找到同目录下的base.py,因此运行无异常 - 运行根目录下的
main.py时,Python的工作目录是项目根目录,此时模型文件里的from base import Base会让Python去根目录下查找base.py,找不到对应模块就抛出ModuleNotFoundError
正确实现步骤
这个方案完全满足「所有模型共用同一个declarative_base实例」「模型与业务代码分离」的要求,具体操作如下:
- 标记models为Python包
在models目录下新建空文件__init__.py,调整后的项目结构如下:. ├── main.py └── models ├── __init__.py ├── base.py ├── building.py └── user.py - 修正模型文件的导入路径
将building.py和user.py中导入Base的语句改为包内相对导入,确保无论从哪个入口启动项目,都能导入同一个Base实例:- 修改
models/building.py的导入行:将from base import Base替换为from .base import Base,完整文件内容如下:from sqlalchemy import Column, Integer, String from .base import Base class Building(Base): __tablename__ = "building" id = Column('id', Integer, primary_key=True, autoincrement=True) address = Column('address', String(50), unique=True) if __name__ == "__main__": print("Imports successful on building.py") - 修改
models/user.py的导入行:将from base import Base替换为from .base import Base,完整文件内容如下:from sqlalchemy import Column, Integer, String from .base import Base class User(Base): __tablename__ = "user" id = Column('id', Integer, primary_key=True, autoincrement=True) username = Column('username', String(50), unique=True) if __name__ == "__main__": print("Imports successful on user.py")
注意:导入语句中开头的
.代表当前文件所属的包目录(即models文件夹),这种相对导入方式不会受启动时工作目录的影响,且所有模型导入的都是base.py中初始化的唯一Base实例,完全符合SQLAlchemy的机制要求,不会出现metadata隔离、create_all()无法识别全部表的问题。 - 修改
- (可选优化)统一模型导出入口
在models/__init__.py中统一导出Base和所有模型类,后续业务代码导入时更简洁,还能保证所有模型都被正确加载到Base的metadata中,文件内容如下:
此时from .base import Base from .building import Building from .user import Usermain.py的导入逻辑可以简化为:from models import Base, User, Building # 后续可直接基于导入的Base执行表创建等操作 # 示例: # from sqlalchemy import create_engine # engine = create_engine("sqlite:///./project.db") # Base.metadata.create_all(bind=engine) print("Imports successful on main.py")
兼容单文件测试的补充方案
如果需要保留单独运行模型文件做测试的能力,二选一即可:
- 推荐方式:在项目根目录下用模块模式运行,不需要改业务代码,执行命令分别为
python -m models.building、python -m models.user - 兼容方式:在两个模型文件的导入部分增加异常捕获,同时适配两种运行场景,以
user.py为例:from sqlalchemy import Column, Integer, String try: # 包导入场景使用相对导入 from .base import Base except ImportError: # 直接运行单文件场景使用同级导入 from base import Base # 后续类定义、测试代码保持不变
内容的提问来源于stack exchange,提问作者Dacromir
相关产品推荐
相关产品推荐

