【问题标题】:Deleting sqlalchemy column attributes, added after class declaration, in successive pytests在连续的 pytests 中删除 sqlalchemy 列属性,在类声明之后添加
【发布时间】:2021-10-15 16:37:14
【问题描述】:

要在数据库元数据中添加一列,在启动 alembic 迁移之前,我在列插入 python 脚本中执行以下操作:

class table(Base):
     __tablename__ = "my_table"
     id = Column(Integer, primary_key = True)
     name = Column(String)

col_name = "nickname"

#also assume  there's another class that has a foreign key that references table.id
related_class = list(inspect(table).relationships)[0].entity
related_tab = list(inspect(table).relationships)[0].entity.local_table

# I create two column objects, since the same one cannot be assigned to two tables
new_column = Column(col_name,type_ = new_type)
new_column2= Column(col_name,type_ = new_type)

table.__table__.append_column(new_column)
table.__mapper__.add_property(col_name,new_column)

#add the column to the related class/table
related_tab.append_column(new_column2)
related_class.add_property(col_name, new_column2)

#then I run my alembic auto revision and upgrade script

基本上,我创建了两个相同的列对象,然后将它们添加到表中,并作为映射器属性。

但是,我在测试数据库时遇到了问题。我的映射类(不是数据库)的状态被重用于下一个测试。但我希望在每次测试开始时都有一个干净的状态。

最后一个测试是向 Base.metadata 添加一个新列,然后创建一个自动生成的修订和升级。在测试结束时降级并不能解决问题。

这是具体内容。

我为每个测试类创建了一个新的引擎/数据库,但元数据/映射器(来自 Base 和类表)仍然包含来自前一组测试的额外列。作为我拆解的一部分,sqlite 数据库文件在测试集/类结束时被删除。

因此,下一组测试的 create_engine() 命令添加了我不想要的额外列。

使用 clear_mapper 不起作用,因为整个表都被删除了,并且无法在其他测试集中找到。

那么,作为拆解的一部分,如何从映射器中删除此列属性?

这是我发现几乎可以工作的方法

table.__mapper__.attrs=ImmutableProperties(
          dict(list(Ref_sheet.__mapper__.attrs.items())[:size-1]
              ))

table.__table__.columns = ColumnCollection(
        (list(table.__table__.columns.items())[:-1])).as_immutable()

如果我遍历列 attributes/keys() 那么最新的列就消失了(在 pytest 类之间)。但是,如果我遍历 table.ma​​pper.all_ORM_descriptors(),仍然会出现应该删除的列。

一个潜在的解决方案是为每组测试创建不同的类(即 table1、table2)。但如果测试变得更大,这将无法扩展,并导致重复代码。

【问题讨论】:

    标签: python sqlalchemy pytest


    【解决方案1】:

    这只是删除类属性的问题,以及曾经通过首先检索它们而关联的列对象。但是,仍然存在 ORM 描述符 - 作为 InstrumentedAttributes。

    但是,删除类属性和关联列就足以让 Base.metadata.create_all() 创建没有删除列的表。需要做的只是在应用迁移脚本之前但在 create_all() 命令之后反映数据库中的表。

    ._columns 之前的下划线(我也假设属性)是列对象,然后将其转换为不可变列集合

    del table.__mapper__._props['new_column']
    #col.name is the name of the column key as it appears in the table
    cols = [col for col in table.__table__._columns if col.name == 'new_column'][0]
    table.__table__._columns.remove(cols)
    
    Base.metadata.create_all()
    table.__table__ = Table(table.__tablename__, Base.metadata, autoload_with = engine)
    
    

    因此,在测试数据库操作以及单个列添加(例如使用 alembic)的 pytest 环境中,只需将上述代码包装在 if 语句中作为引擎设置夹具的一部分。

    if list(table.__table__.columns.keys())[-1] == 'new_column':
       del table.__mapper__._props['new_column']
       cols = [col for col in table.__table__._columns if col.name == 'new_column'][0]
       table.__table__._columns.remove(cols)
       #do the same for related tables
    
    Base.metadata.create_all()
    table.__table__ = Table(table.__tablename__, Base.metadata, autoload_with = engine)
    
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-23
      • 2016-11-11
      • 2021-09-22
      • 1970-01-01
      • 1970-01-01
      • 2011-03-23
      • 1970-01-01
      • 2016-07-11
      相关资源
      最近更新 更多