source

alembic.ini 외부에 alembic 연결 문자열을 저장할 수 있습니까?

lovecheck 2023. 8. 10. 18:56
반응형

alembic.ini 외부에 alembic 연결 문자열을 저장할 수 있습니까?

SQL Chemy에 Alembic을 사용하고 있습니다.SQLAlchemy에서는 버전 코드로 연결 문자열을 저장하지 않는 패턴을 따르는 경향이 있습니다.에 저는 파일을 있습니다.secret.py모든 기밀 정보가 포함되어 있습니다.는 이 을 내 이파일이내넣파다습니에일을에 씁니다..gitignoreGitHub에서 끝나지 않습니다.

이 패턴은 잘 작동하지만 이제는 마이그레이션에 알렘빅을 사용하기 시작했습니다.연결 문자열을 숨길 수 없는 것 같습니다. 대에신으로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

데이터베이스에 대한 사용자 이름/암호 정보가 있는 파일을 실수로 커밋할 수 있습니다.이 연결 문자열을 한 곳에 저장하여 실수로 버전 제어에 커밋할 위험을 피하고 싶습니다.

어떤 선택지가 있습니까?

저는 어제 똑같은 문제를 겪었고 다음과 같은 해결책을 찾았습니다.는 다음작수다니에서 .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(...)으로 근적으덮니다어씁로를 덮어씁니다.sqlalchemy.url의 키[alembic]의한부의 한 alembic.ini파일입니다. 제 구성에서는 그냥 검은색으로 남겨둡니다.

위해 수 가장 에 보간 하는 것입니다.alembic.ini 이 을 파일 및이 b) 러한 값설정에 합니다.env.py

레엠빅

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"))

...

기호 문서에서는 다음을 사용할 것을 권장합니다.create_engine(코드에서 sqlalchemy.url을 수정하는 대신) 데이터베이스 URL을 사용합니다.

또한 새 URL을 사용하려면 run_migrations_migrations_migrations를 수정해야 합니다. Allan Simon은 블로그에 예제를 가지고 있지만 요약하면 env.py 을 다음과 같이 수정하십시오.

  1. URL을 가져오는 공유 기능을 제공합니다(여기서 URL은 명령줄에서 가져옵니다).

    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. 오프라인 모드에서 URL 사용:

    def run_migrations_offline():
        ...
        url = get_url()
        context.configure(
            url=url, target_metadata=target_metadata, literal_binds=True)
        ...
    
  3. 에서 다을사여 모드서 URL을 합니다.create_engineengine_from_config:

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

그래서 작동하는 것으로 보이는 것은 엔진 생성을 재구현하는 것입니다.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입니다.이 키는 실제로 개발자가 사용자 지정할 수 있는 파일인 "module" 구성과 관련된 env.py 파일 내에서만 참조됩니다.다중 데이터베이스 구성은 여기서 여러 키에 응답하거나 파일의 다른 섹션을 참조할 수 있습니다.

멀티 데이터베이스에 대한 관리 방법을 잠시 찾고 있었습니다.

여기 제가 한 일이 있습니다.나는 두 개의 데이터베이스를 가지고 있습니다: 로그와 ohlc.

문서에 따르면, 저는 알람을 그렇게 설정했습니다.

alembic init --template multidb

레엠빅

databases = logs, ohlc
[logs]
sqlalchemy.url = postgresql://botcrypto:botcrypto@localhost/logs
[ohlc]
sqlalchemy.url = postgresql://botcrypto:botcrypto@localhost/ohlc

env.py

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

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')

# overwrite alembic.ini db urls from the config file
settings_path = os.environ.get('SETTINGS')
if settings_path:
    with open(settings_path) as fd:
        settings = conf.load(fd, context=os.environ) # loads the config.yml
    config.set_section_option("ohlc", "sqlalchemy.url", settings["databases"]["ohlc"])
    config.set_section_option("logs", "sqlalchemy.url", settings["databases"]["logs"])
else:
    logger.warning('Environment variable SETTINGS missing - use default alembic.ini configuration')
[...]

config.yml

databases:
    logs: postgresql://botcrypto:botcrypto@127.0.0.1:5432/logs
    ohlc: postgresql://botcrypto:botcrypto@127.0.0.1:5432/ohlc

사용.

SETTINGS=config.yml alembic upgrade head

그것이 도움이 되기를!

MultiDB 설정의 경우(SingleDB의 경우 동일) config를 사용할 수 있습니다.alembic.ini 파일에서 데이터베이스 URL 값을 수정하려면 set_section_option("section"n_name"), 'db_name', 'db_URL')을 지정합니다.

예:

기호의안에

[engine1]
sqlalchemy.url = 

[engine2]
sqlalchemy.url = 

그리고나서,

env.py

config = context.config

config.set_section_option('engine1', 'sqlalchemy.url', os.environ.get('URL_DB1'))
config.set_section_option('engine2', 'sqlalchemy.url', os.environ.get('URL_DB2'))

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

로컬 컴퓨터에서 마이그레이션을 실행하고 있기 때문에 이 문제에도 직면했습니다.을 제솔션섹다배것다입니에 넣는 입니다.alembic.ini데이터베이스 구성(인증 정보 포함)을 저장합니다.

[local]
host = localhost
db = dbname

[test]
host = x.x.x.x
db = dbname

[prod]
host = x.x.x.x
db = dbname

▁the▁in에 .env.py사용자가 환경을 선택하고 자격 증명을 입력하라는 메시지를 표시할 수 있습니다.

from alembic import context
from getpass import getpass

...

envs = ['local', 'test', 'prod']

print('Warning: Do not commit your database credentials to source control!')
print(f'Available migration environments: {", ".join(envs)}')

env = input('Environment: ')

if env not in envs:
    print(f'{env} is not a valid environment')
    exit(0)

env_config = context.config.get_section(env)
host = env_config['host']
db = env_config['db']

username = input('Username: ')
password = getpass()
connection_string = f'postgresql://{username}:{password}@{host}/{db}'

context.config.set_main_option('sqlalchemy.url', connection_string)

전체 팀이 액세스할 수 있는 암호 관리자 또는 사용 가능한 구성/비밀 저장소에 자격 증명을 저장해야 합니다.하지만, 이 접근법으로 비밀번호는 당신의 로컬 클립보드에 노출됩니다 - 훨씬 더 나은 접근법은.env.py구성/비밀 저장소 API에 직접 연결하고 사용자 이름/암호를 직접 꺼내지만 타사 종속성이 추가됩니다.

또 다른 해결책은 템플릿 alembic.ini.dist 파일을 생성하여 버전 코드로 추적하는 이지만, eembic은 무시합니다.VCS의 ini.

alembic.ini.dist에 기밀 정보를 추가하지 마십시오.

sqlalchemy.url = ...

플랫폼에 코드를 배포할 때 alembic.ini.dist를 alembic.ini에 복사하고(이 파일은 VCS에서 추적되지 않음) 기호를 수정합니다.플랫폼의 자격 증명을 가진 ini.

Doug T.가 말했듯이 env.py 을 편집하여 ini 파일이 아닌 다른 곳에서 URL을 제공할 수 있습니다.새 엔진을 생성하는 대신 추가 엔진을 전달할 수 있습니다.urlengine_from_config함수(나중에 kwarg는 ini 파일에서 가져온 옵션에 병합됨).이 경우 암호화된 암호를 ini 파일에 저장하고 ENV 변수에 저장된 암호 구문을 통해 런타임에 암호를 해독할 수 있습니다.

connectable = engine_from_config(                 
    config.get_section(config.config_ini_section),
    prefix='sqlalchemy.',                         
    poolclass=pool.NullPool,                      
    url=some_decrypted_endpoint)                                   

가 있었던 은 제게효있옵션은을 사용하는 이었습니다.set_main_option그리고 그 자리를 떠나십시오.sqlalchemy.url =의 공백이 있는.alembic.ini

from config import settings

config.set_main_option(
    "sqlalchemy.url", settings.database_url.replace("postgres://", "postgresql+asyncpg://", 1))

stings는 env 파일의 변수를 가져오는 데 사용하는 구성 파일의 클래스입니다. os.environment.get()이 창에서 환경 값을 반환하지 않습니까?자세한 내용은 다음을 참조하십시오.os.environ.get그러나 sqlalchemy.ex와 같은 오류를 방지하려면 변수를 내보내야 합니다.인수 오류: 문자열에서 rfc1738 URL을 구문 분석할 수 없습니다.

TomDotTom의 답변을 바탕으로 이 솔루션을 고안했습니다.

을 합니다.env.py 파일로 합니다.

config = context.config
config.set_section_option("alembic", "sqlalchemy.url",
                          os.environ.get("DB_URL", config.get_section_option("alembic", "sqlalchemy.url"))) # type: ignore

이 작업은 다음 작업보다 우선합니다.sqlalchemy.urlalembic에 있습니다.DB_URLenvironment variable을 합니다. 그렇지 됩니다.alembic.ini

그런 다음 다른 데이터베이스를 가리키는 마이그레이션을 실행할 수 있습니다.

DB_URL=driver://user:pass@host:port/dbname alembic upgrade head

해서 그고계사용속을 사용하세요.alembic upgrade head에.

여기서 모든 답을 시도해봤지만 소용이 없었습니다.그럼 아래와 같이 제가 직접 처리하려고 합니다.

.ini 파일:

# A generic, single database configuration.

[alembic]
# path to migration scripts
script_location = alembic

# template used to generate migration files
file_template = %%(rev)s_%%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d_%%(minute).2d_%%(second).2d

# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =

# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false

# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false

# version location specification; this defaults
# to alembic/versions.  When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions

# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8

databases = auth_engine


[auth_engine]
sqlalchemy.url = mysql+mysqldb://{}:{}@{}:{}/auth_db

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

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

.env 파일(내 프로젝트의 루트 폴더에 있음):

DB_USER='root'
DB_PASS='12345678'
DB_HOST='127.0.0.1'
DB_PORT='3306'

env.py 파일:

from __future__ import with_statement

import os
import re
import sys
from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context

DB_USER = os.getenv("DB_USER")
DB_PASS = os.getenv("DB_PASS")
DB_HOST = os.getenv("DB_HOST")
DB_PORT = os.getenv("DB_PORT")

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

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

# gather section names referring to different
# databases.  These are named "engine1", "engine2"
# in the sample .ini file.
db_names = config.get_main_option('databases')

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata

sys.path.append(os.path.join(os.path.dirname(__file__), "../../../"))
from db_models.auth_db import auth_db_base

target_metadata = {
    'auth_engine': auth_db_base.auth_metadata
}


# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline():
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """
    engines = {}
    for name in re.split(r',\s*', db_names):
        engines[name] = rec = {}
        section = context.config.get_section(name)
        url = section['sqlalchemy.url'].format(DB_USER, DB_PASS, DB_HOST, DB_PORT)
        section['sqlalchemy.url'] = url
        rec['url'] = url
        # rec['url'] = context.config.get_section_option(name, "sqlalchemy.url")

    for name, rec in engines.items():
        print("Migrating database %s" % name)
        file_ = "%s.sql" % name
        print("Writing output to %s" % file_)
        with open(file_, 'w') as buffer:
            context.configure(url=rec['url'], output_buffer=buffer,
                              target_metadata=target_metadata.get(name),
                              compare_type=True,
                              compare_server_default=True
                              )

            with context.begin_transaction():
                context.run_migrations(engine_name=name)


def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    engines = {}
    for name in re.split(r',\s*', db_names):
        engines[name] = rec = {}
        section = context.config.get_section(name)
        url = section['sqlalchemy.url'].format(DB_USER, DB_PASS, DB_HOST, DB_PORT)
        section['sqlalchemy.url'] = url
        rec['engine'] = engine_from_config(
            section,
            prefix='sqlalchemy.',
            poolclass=pool.NullPool)

    for name, rec in engines.items():
        engine = rec['engine']
        rec['connection'] = conn = engine.connect()
        rec['transaction'] = conn.begin()

    try:
        for name, rec in engines.items():
            print("Migrating database %s" % name)
            context.configure(
                connection=rec['connection'],
                upgrade_token="%s_upgrades" % name,
                downgrade_token="%s_downgrades" % name,
                target_metadata=target_metadata.get(name),
                compare_type=True,
                compare_server_default=True
            )
            context.run_migrations(engine_name=name)

        for rec in engines.values():
            rec['transaction'].commit()
    except:
        for rec in engines.values():
            rec['transaction'].rollback()
        raise
    finally:
        for rec in engines.values():
            rec['connection'].close()


if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()

소원은 다른 사람을 도울 수 있습니다.

env.py 덧셈을 하라.

config.set_main_option('sqlalchemy.url', os.environ['DB_URL'])

끝나고

config = context.config

맘에 들다

config = context.config
config.set_main_option('sqlalchemy.url', os.environ['DB_URL'])

다음과 같이 실행합니다.

DB_URL="mysql://atuamae:de4@127.0.0.1/db" \
  alembic upgrade head

언급URL : https://stackoverflow.com/questions/22178339/is-it-possible-to-store-the-alembic-connect-string-outside-of-alembic-ini

반응형