连续pytest测试中如何删除类声明后添加的SQLAlchemy列属性
解决方案
方案1:测试级元数据隔离(最推荐,无清理负担)
你遇到的状态残留本质是全局Base和模型类被多个测试复用导致的,最优解是每次测试都生成独立的Base实例和对应的模型类,从根源避免状态污染。
你可以用pytest fixture实现,不需要重复写模型代码:
import pytest from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy.orm import declarative_base, relationship @pytest.fixture(scope="function") # 每个测试函数都生成新的实例 def test_models(): Base = declarative_base() class Table(Base): __tablename__ = "my_table" id = Column(Integer, primary_key=True) name = Column(String) class RelatedClass(Base): __tablename__ = "related_table" id = Column(Integer, primary_key=True) table_id = Column(Integer, ForeignKey("my_table.id")) table = relationship("Table") return Base, Table, RelatedClass
测试时直接调用这个fixture即可,每次拿到的都是干净的元数据和映射类,测试结束后fixture销毁,不会有任何残留,也不需要手动写teardown逻辑,扩展性极强,新增测试也不会互相影响。
方案2:手动清理动态添加的列(适合必须复用全局模型的场景)
你之前的操作直接修改SQLAlchemy的内部私有集合,没有触发mapper的更新逻辑,才会导致描述符残留。应该用SQLAlchemy官方提供的API执行清理,步骤如下:
清理步骤
- 先移除映射类的mapper属性
- 再移除表结构中的列定义
- 最后删除类本身挂载的ORM描述符
示例teardown代码
def teardown_columns(table_cls, related_cls, col_name): # 清理主表 # 1. 移除mapper属性(官方API,会自动清理内部映射) table_cls.__mapper__.remove_property(col_name) # 2. 移除表中的列 del table_cls.__table__.columns[col_name] table_cls.__table__._columns.remove(table_cls.__table__.c[col_name]) # 3. 删除类上残留的描述符 if col_name in table_cls.__dict__: delattr(table_cls, col_name) # 同理清理关联表 related_cls.__mapper__.remove_property(col_name) del related_cls.__table__.columns[col_name] related_cls.__table__._columns.remove(related_cls.__table__.c[col_name]) if col_name in related_cls.__dict__: delattr(related_cls, col_name)
执行完上述清理后,all_ORM_descriptors()就不会再出现被删除的列了。
注意事项
如果你的测试场景是验证Alembic自动生成迁移的正确性,更推荐用方案1,干净的元数据能保证每次生成的迁移脚本都是当前测试场景下的正确结果,不会被之前测试残留的列干扰。
内容的提问来源于stack exchange,提问作者golden_curry2020
相关产品推荐
相关产品推荐

