【问题标题】:How can I switch between schemas at the point of a query如何在查询时在模式之间切换
【发布时间】:2020-09-29 19:28:47
【问题描述】:

通过 MRE,我有一个名为 query 的简单模块,它向 Bet 表添加一条记录:

from container_test import Bet, DataAccessLayer

def main():
    dal = DataAccessLayer()
    dal.create_session()
    query_bets(dal)

def add_new_id(dal):
    new_id = {"id_": 3}
    dal.session.add(Bet(**new_id))
    dal.session.commit()

if __name__ == "__main__":
    main()

container_test 包含以下内容:

from sqlalchemy import Column, Integer, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Bet(Base):

    __tablename__ = "bet"
    __table_args__ = ({"schema": "belgarath", "extend_existing": True})

    id_ = Column(Integer, primary_key=True)

class DataAccessLayer():

    def __init__(self):
        conn_string = "mysql+mysqlconnector://root:root@localhost/"
        self.engine = create_engine(conn_string)

    def create_session(self):
        Base.metadata.create_all(self.engine)
        Session = sessionmaker()
        Session.configure(bind=self.engine)
        self.session = Session()

我希望能够测试query_bets 函数,为此我在另一个文件中创建了一个简单的pytest 函数:

def test_add_new_id(
    create_dal, # creates an instance of a dal via conftest
    setup_teardown, # creates a blank belgarath_test schema and deletes afterwards via conftest
):
    expected = 3
    sandpit.add_new_id(create_dal)
    actual = query_bet(create_dal) # function that will return a scalar result
    assert actual == expected

但是,我希望能够将标准 belgarath 架构更改为测试架构; belgarath_test 所以我不会冒主数据库完整性的风险。我想我需要某种可以在test_add_new_id 测试功能中激活的“开关”,但我不确定如何创建。

我一直在尝试遵循这篇文章中提出的解决方案:How do I change the schema for both a table and a foreign key? 但是我很挣扎。我不明白我需要在 test_add_new_id 测试函数中做什么来更改架构。

作为参考,我根据解决方案创建了以下模块:

container_test:


from sqlalchemy import Column, Integer, create_engine
from sqlalchemy.orm import sessionmaker

from base_class import SCHEMA_TEST, Base, declared_attr


class Bet(Base):

    @declared_attr
    def __table_args__(cls):
        return {'schema': SCHEMA_TEST}

    __tablename__ = "bet"

    id_ = Column(Integer, primary_key=True)


class DataAccessLayer():

    def __init__(self):
        conn_string = "mysql+mysqlconnector://root:root@localhost/"
        self.engine = create_engine(conn_string)

    def create_session(self):
        Base.metadata.create_all(self.engine)
        Session = sessionmaker()
        Session.configure(bind=self.engine)
        self.session = Session()

base_class(尝试使用__init__.py时出错):

from sqlalchemy.ext.declarative import declarative_base, declared_attr

SCHEMA_MAIN = 'belgarath'  # figure out how you want to retrieve this
SCHEMA_TEST = 'belgarath_test'


class _Base():

    @declared_attr
    def __table_args__(cls):
        return {'schema': SCHEMA_MAIN}


Base = declarative_base(cls=_Base)
Base.metadata.schema = SCHEMA_MAIN

这成功地将belgarath 更改为belgarath_test,但我只希望在运行test_add_new_id 测试函数时发生这种情况。

【问题讨论】:

  • 运行测试时,为什么不将您的应用程序指向数据库的单独备份副本?
  • 我很乐意这样做,但我仍然需要将测试脚本指向该数据库?我该怎么做?
  • @Gord - 抱歉,我应该感谢您的回复!
  • 您可以根据您是在“测试模式”还是“生产模式”中运行来构建连接 URI。这可以由环境变量、命令行参数,甚至机器本身的名称来控制。
  • 非常感谢您的帮助!挑战是我在数据库中有两个模式(belgarathoncourt),所以我认为我不能在连接中指定特定的模式?我还将使用oncourt_testoncourt 构建测试脚本

标签: python sqlalchemy


【解决方案1】:

与其在同一个数据库的不同模式中运行测试,不如在不同的数据库中运行测试?

这也将改善测试隔离,因为测试将不再有生产数据库的密码,因此有缺陷的测试将无法破坏产品数据。

【讨论】:

  • 嗨。感谢您的回复。这只是一个个人项目,所以我希望避免拥有实际测试和生产数据库的复杂性,因为它们都必须坐在我家的 PC 上。我可以做些什么来在短期内实现“切换”吗?
【解决方案2】:

从 sqlalchemy 1.1 开始,您可以使用以下命令在运行时轻松更改架构:schema_translation_map

正如在cmets中讨论过的那样,单个会话需要使用4个模式,不建议重复创建会话而简单地使用:

session.connection(execution_options={"schema_translate_map": {"belgarath": "belgarath_test"}})

上述语句会将架构切换为belgarath_test,同样可以使用oncourtoncourt_test

【讨论】:

  • 嘿。谢谢你!参数实际上应该是:{"schema": "belgarath_test"}?我将面临的问题是我在同一个数据库中有两个模式,我需要将它们都更改为它们的测试版本。不确定以上是否可行?
  • @Jossy 要更改会话的架构,使用的脚本是:session.connection(execution_options={"schema_translate_map": {"schema": "belgarath_test"}}) 您可以在执行测试函数之前更改架构并在最后将其恢复为之前的架构。
  • 嗨。我在会话中需要两个模式 - belgarathoncourt 需要分别切换到 belgarath_testoncourt_testschema_translate_map 似乎每次会话只能更改一个架构?
  • @Jossy,您是否尝试过在同一会话中多次使用我提到的功能?它应该可以工作任意次数。
  • 你真是个天才!上面的答案不起作用,但它让我走上了正轨。第二次运行create_session 会覆盖第一个会话。但是,我可以运行create_session,然后运行session.connection(execution_options={"schema_translate_map": {"belgarath": "belgarath_test"}}),然后运行oncourt 等效项,我们就在那里!你不知道我试图解决这个问题多久了!!!您是否要修改答案以包括双模式更改并且我将标记为已接受?
猜你喜欢
  • 1970-01-01
  • 2021-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多