可以在alembic.ini之外存储alembic连接字符串吗?

135 投票
15 回答
54334 浏览
提问于 2025-04-17 20:48

我在用Alembic配合SQLAlchemy。通常,我的做法是把连接数据库的字符串(也就是连接信息)和版本代码分开存储。我会创建一个叫secret.py的文件,把所有的机密信息放在里面。然后我把这个文件名加到.gitignore里,这样它就不会被上传到GitHub上。

这个方法一直都挺好用,但现在我开始用Alembic来处理数据库迁移了。问题是,我发现我不能把连接字符串隐藏起来。在alembic.ini文件里,我需要把连接字符串放在一个配置参数里:

# the 'revision' command, regardless of autogenerate
# revision_environment = false

sqlalchemy.url = driver://user:pass@localhost/dbname

# Logging configuration
[loggers]
keys = root,sqlalchemy,alembi

我担心会不小心把包含数据库用户名和密码的信息的文件提交上去。我希望能把这个连接字符串放在一个地方,这样就能避免不小心把它提交到版本控制系统里的风险。

我还有哪些选择呢?

15 个回答

7

env.py:

from alembic.config import Config

alembic_cfg = Config()
alembic_cfg.set_main_option("sqlalchemy.url", getenv('PG_URI'))

https://alembic.sqlalchemy.org/en/latest/api/config.html

11

看起来可以通过在 env.py 文件中重新实现引擎创建来解决问题,这个文件显然是用来进行这种自定义的地方,而不是在 ini 文件中使用 sqlalchemy 的连接字符串:

engine = engine_from_config(
            config.get_section(config.config_ini_section),
            prefix='sqlalchemy.',
           poolclass=pool.NullPool)

你可以替换并指定自己的引擎配置:

import store
engine = store.engine

实际上,文档中 似乎暗示 这样做是可以的:

sqlalchemy.url - 这是一个通过 SQLAlchemy 连接到数据库的 URL。这个键实际上只在特定于“通用”配置的 env.py 文件中被引用;这个文件可以由开发者自定义。多个数据库配置可能会对应多个键,或者可能引用文件的其他部分。

41

Alembic 的文档建议使用 create_engine 来创建数据库连接,直接用数据库的地址,而不是像在代码中修改 sqlalchemy.url

另外,你还需要修改 run_migrations_offline 这个函数,让它使用新的数据库地址。Allan Simon 在他的博客上有一个例子,但简单来说,你需要修改 env.py 文件,步骤如下:

  1. 提供一个共享的函数来获取数据库地址(这里是从命令行获取的):

    def get_url():
        url = context.get_x_argument(as_dictionary=True).get('url')
        assert url, "Database URL must be specified on command line with -x url=<DB_URL>"
        return url
    
  2. 在离线模式下使用这个地址:

    def run_migrations_offline():
        ...
        url = get_url()
        context.configure(
            url=url, target_metadata=target_metadata, literal_binds=True)
        ...
    
  3. 在在线模式下使用 create_engine 来代替 engine_from_config

    def run_migrations_online():
        ...
        connectable = create_engine(get_url())
        with connectable.connect() as connection:
        ...
    
96

为了避免把我的用户名和密码写进代码里,我想到的最简单的方法就是:a) 在 alembic.ini 文件里加入插值字符串,b) 在 env.py 文件里设置这些插值的值。

alembic.ini

sqlalchemy.url = postgresql://%(DB_USER)s:%(DB_PASS)s@35.197.196.146/nozzle-website

env.py

import os

from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# here we allow ourselves to pass interpolation vars to alembic.ini
# fron the host env
section = config.config_ini_section
config.set_section_option(section, "DB_USER", os.environ.get("DB_USER"))
config.set_section_option(section, "DB_PASS", os.environ.get("DB_PASS"))

...
118

我昨天也遇到了同样的问题,找到了解决办法。我的做法是在 alembic/env.py 文件中这样写:

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# this will overwrite the ini-file sqlalchemy.url path
# with the path given in the config of the main code
import config as ems_config
config.set_main_option('sqlalchemy.url', ems_config.config.get('sql', 'database'))

ems_config 是一个外部模块,用来存放我的配置数据。

config.set_main_option(...) 这个命令主要是用来覆盖 alembic.ini 文件中 [alembic] 部分的 sqlalchemy.url 这个选项。在我的配置中,我干脆把它留空。

撰写回答