如何有选择地初始化模块级对象?

时间:2017-09-24 20:17:44

标签: python python-3.x sqlalchemy

我有一个使用SQLAlchemy的Flask Web应用程序。在生产中支持它的数据库是Postgres,但出于单元测试的目的,我还希望能够针对SQLite初始化它。

我使用alembic进行数据库架构更改。出于这个原因,我决定不支持SQLite进行生产,因为制作alembic支持SQLite比我想要的更麻烦。但是,我仍然希望能够在SQLite中从头开始创建表,以进行测试;这不会调用alembic,而只是使用SQLAlchemy的create_all()

我正在使用声明性模型,为了使alembic更好地工作,我按照文档中的建议设置了约束的命名约定:

naming = {
    "ix": 'ix_%(column_0_label)s',
    "uq": "uq_%(table_name)s_%(column_0_name)s",
    "ck": "ck_%(table_name)s_%(constraint_name)s",
    "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    "pk": "pk_%(table_name)s",
}

Base = declarative_base(metadata=MetaData(naming_convention=naming))

class Model1(Base):
    ...

这在Postgres中运行良好,但在SQLite中失败;显然,命名约定在某种程度上是不兼容的。 (这可能与我的一些模型有Boolean列的事实有关,这两列显然在两个引擎中处理不同。)

由于我实际上并不需要SQLite进行生产,并且如果我这样做就不能使用alembic,我倾向于简单地删除该情况下的命名约定并让它在运行测试时使用默认名称。但是,由于这都是在模块顶层完成的(并且必须是,为了使用Base作为基类),我找不到一种方法来进行这种区分。理想情况下,我可以在导入模型之前在测试套件中设置某种标志,这会导致该模块使用默认元数据创建Base,而不是启用命名约定。但是我无法在导入时找到一种方法来进行这种沟通。

如何在导入时传入数据以控制声明性基础的创建?或者,是否有一种方法可以轻松地使SQLite在启用命名约定的情况下工作?

1 个答案:

答案 0 :(得分:0)

我最终通过环境变量来解决这个问题。在项目模块中:

if os.environ.get('MYPROJECT_TESTING') == '1':
    Base = declarative_base()
else:
    Base = declarative_base(metadata=MetaData(naming_convention=naming)

然后在测试中:

os.environ['MYPROJECT_TESTING'] = '1'
import myproject

这样可以正常工作,但您必须确保不要将MYPROJECT_TESTING设置在shell环境中,或者除了在测试脚本中自动设置外,还要设置它。