alembic没有在模式中生成表格。

huangapple go评论60阅读模式
英文:

alembic not generating tables in schema

问题

我有一个包含三个容器的Docker设置。
A) uvicorn/fastAPI应用程序
B) MongoDB
C) PostgreSQL数据库

这三个都正常工作。问题出现在alembic上,它托管在容器A中,与我的应用程序的其余部分一起,对容器C中的PostgreSQL进行更改。

我有一个DatabaseSession类,用于连接数据库。
我可以连接并运行命令。它似乎在响应。
以下是该类中的连接方法:

def _connect_db(self, db_schema_override: str = None):
        schema = self.schema if not db_schema_override else db_schema_override
        try:
            engine = create_engine(
                self.db_url, connect_args={"options": "-csearch_path={}".format(schema)}
            )
            SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
            session = SessionLocal()
            metadata = MetaData()
            metadata.reflect(bind=engine)
            metadata.create_all(engine)
            return engine, session
        except (AttributeError, ValueError):
            raise

以下是我的alembic迁移文件:

def upgrade_db(db_schema: str, revision: str = "head") -> None:
    db = DatabaseSession(db_schema)
    db.db_data.maintenance_mode = True
    db.db_data.save()
    _config = config.Config("path/to/file/alembic.ini")
    _config.set_main_option("sqlalchemy.url", db.db_url)
    with db.engine.begin() as cnxn:
        _config.attributes["connection"] = cnxn
        _config.attributes["schema"] = db_schema.lower()
        command.upgrade(_config, revision)
        db.db_data.maintenance_mode = False
        db.db_data.save()

以下是我的alembic env.py文件中的run_migrations_online函数:

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.

    """
    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )
    db_schema = config.attributes.get("schema", "public")

    with connectable.connect() as connection:
        connection.execute(text(f'CREATE SCHEMA IF NOT EXISTS "{db_schema}"'))
        connection.execute(text(f"SET search_path TO '{db_schema}'"))
        connection.dialect.default_schema_name = db_schema

        context.configure(
            connection=connection,
            target_metadata=target_metadata,
            include_schemas=True,
        )

        with context.begin_transaction():
            context.run_migrations()

正如你所看到的,我在每个数据库中使用模式来进一步分离关注点。
基本上,我使用MongoDB来存储包括数据库名称和模式在内的数据库记录。
我已检查了PostgreSQL的权限,我可以使用相同的用户登录到PostgreSQL并运行命令来创建表格。

我可以从Python中使用我的DatabaseSession类访问PostgreSQL并运行命令,但似乎无法创建任何东西。
当我从Python解释器中运行'create table'命令时,它会注册已发送命令的消息。
但是,如果我在另一个窗口中运行psql CLI中的\dt或其他任何内容,不会显示任何更改。
如果我尝试再次使用DatabaseSession类运行'create table'命令,它将会说此表已存在。
就像它没有提交命令一样。如果我终止会话或执行回滚,我可以再次成功运行命令。
我是否应该提交这个命令?

我也尝试只运行alembic upgrade head。同样的问题。
它说已经顺利运行了所有的迁移,没有错误。但是没有更改保留在数据库中。

此外,Alembic似乎没有创建版本表,用于存储迁移ID。

我以前在过去的版本中进行了完全相同的设置(较早版本的SQLAlchemy)。我不确定现在有什么不同,以至于我不能使其工作。

总结

  • 使用数据库/模式设置
  • 可以连接并运行命令,作为模式内的用户通过CLI
  • 可以从Python连接并运行命令,但没有东西持久保存
  • 没有版本表存在于我的数据库/模式中

非常感谢任何帮助。谢谢。

英文:

I have a docker setup consisting of three containers.
A) uvicorn/fastAPI app
B) mongoDB
C) Postgres DB

All three working. This is a problem with alembic, hosted in container A with the rest of my app, making changes to postgres in container C.

I have a DatabaseSession class that connects to the DB.
I can connect just fine and run commands. It does seem to be responding.
Here is the connect method within that class:

def _connect_db(self, db_schema_override: str = None):
        schema = self.schema if not db_schema_override else db_schema_override
        try:
            engine = create_engine(
                self.db_url, connect_args={"options": "-csearch_path={}".format(schema)}
            )
            SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
            session = SessionLocal()
            metadata = MetaData()
            metadata.reflect(bind=engine)
            metadata.create_all(engine)
            return engine, session
        except (AttributeError, ValueError):
            raise

here is my alembic migrations file

def upgrade_db(db_schema: str, revision: str = "head") -> None:
    db = DatabaseSession(db_schema)
    db.db_data.maintenance_mode = True
    db.db_data.save()
    _config = config.Config("path/to/file/alembic.ini")
    _config.set_main_option("sqlalchemy.url", db.db_url)
    with db.engine.begin() as cnxn:
        _config.attributes["connection"] = cnxn
        _config.attributes["schema"] = db_schema.lower()
        command.upgrade(_config, revision)
        db.db_data.maintenance_mode = False
        db.db_data.save()

here is my alembic env.py file run_migrations_online function:

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.

    """
    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )
    db_schema = config.attributes.get("schema", "public")

    with connectable.connect() as connection:
        connection.execute(text(f'CREATE SCHEMA IF NOT EXISTS "{db_schema}"'))
        connection.execute(text(f"SET search_path TO '{db_schema}'"))
        connection.dialect.default_schema_name = db_schema

        context.configure(
            connection=connection,
            target_metadata=target_metadata,
            include_schemas=True,
        )

        with context.begin_transaction():
            context.run_migrations()

as you can see, Im utilizing schemas within each database to further seperate concerns.
Im basically using mongodb to store DB records including database name and schema.
Ive checked permissions on postgres, i can log into postgres and run commands as the same user and create tables.

I can access postgres from python using my DatabaseSession class and run commands as that same user. But I cannot seem to create anything.

When i run a 'create table' command using my DatabaseSession class from python interpreter, it registers the command was sent.
But if i run \dt or anything in the psql CLI in another window, no changes show up.
If i try to rerun the 'create table' command using the DatabaseSession class again, itll this time say this table already exists.

Its like its not committing the command. If i kill the session, or do a rollback, I can rerun the command just fine once.
Am i supposed to be commiting this?

ive also tried just running alembic upgrade head. Same issue.
It says it ran all the migrations just fine. no errors. but no changes persist in the DB.

additionally, Alembic does not appear to be creating the versions table where it houses the migration ID.

ive done this EXACT same setup in the past (earlier version of sqlalchemy). Im not exactly sure whats so different now that i cant seem to get this work.

Summary

  • using database/schema setup
  • can connect and run commands as user within schema via CLI
  • can connect and run commands from python. but nothing persists
  • no alembic version table exists within my database/schema

any help is greatly appreciated. thank you

答案1

得分: 1

我的下面的'event'示例似乎在运行,但你说得对。 当你在引擎上设置echo=True时,你的示例似乎只是回滚。

你的示例在SqlAlchemy>=2中似乎不起作用,只在SqlAlchemy<2中起作用。

在考虑了这一点后,似乎如果你在设置dialect.default_schema_name = db_schema之后添加connection.commit(),那么你的代码似乎可以在SqlAlchemy 2中工作。

我创建了一个新的alembic项目,将sqlalchemy的url设置为测试的postgresql数据库。

然后我:

  1. 删除任何版本
  2. 删除数据库
  3. 创建数据库
  4. 运行alembic revision --autogenerate
  5. 运行alembic upgrade head

你的版本似乎回滚。

我从setting-alternate-search-paths-on-connect中获取了@event

from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool
from sqlalchemy.sql import text

from alembic import context

# 这是Alembic配置对象,它提供了对正在使用的.ini文件中的值的访问。
config = context.config
config.attributes['schema'] = 'myschema'

# 解释Python日志的配置文件。
# 这一行基本上设置了记录器。
if config.config_file_name is not None:
    fileConfig(config.config_file_name)

# 在这里添加你的模型的MetaData对象
# 用于支持'autogenerate'
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from sqlalchemy import MetaData
from sqlalchemy import Table, Column, Integer

metadata = MetaData()
users_t = Table(
    "users",
    metadata,
    Column("user_id", Integer, primary_key=True),
)

target_metadata = metadata

# 根据env.py的需求,从配置中获取的其他值可以获取:
# my_important_option = config.get_main_option("my_important_option")
# ...等等。

def run_migrations_offline() -> None:
    """以'offline'模式运行迁移。

    这样就只配置了一个URL的上下文
    而不是一个引擎,尽管引擎也可以
    在这里使用。通过跳过引擎的创建
    我们甚至不需要一个可用的DBAPI。

    在这里对context.execute()的调用将给定的字符串发送到
    脚本输出。

    """
    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=True,
        dialect_opts={"paramstyle": "named"},
    )

    with context.begin_transaction():
        context.run_migrations()

def run_migrations_online() -> None:
    """以'online'模式运行迁移。

    在这种情况下,我们需要创建一个引擎
    并将一个连接与上下文关联起来。

    """
    connectable = engine_from_config(
        config.get_section(config.config_ini_section, {}),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
        echo=True,
    )
    db_schema = config.attributes.get("schema", "public")

    # 在'event'和'inline'之间切换这个
    schema_strategy = 'inline'

    if schema_strategy == 'event':
        from sqlalchemy import event
        @event.listens_for(connectable, "connect", insert=True)
        def set_search_path(dbapi_connection, connection_record):
            existing_autocommit = dbapi_connection.autocommit
            dbapi_connection.autocommit = True
            cursor = dbapi_connection.cursor()
            cursor.execute(f'CREATE SCHEMA IF NOT EXISTS {db_schema}')
            cursor.execute(f"SET SESSION search_path='{db_schema}'")
            cursor.close()
            dbapi_connection.autocommit = existing_autocommit

    with connectable.connect() as connection:

        if schema_strategy == 'inline':
            connection.execute(text(f'CREATE SCHEMA IF NOT EXISTS "{db_schema}"'))
            connection.execute(text(f"SET search_path TO '{db_schema}'"))
            connection.dialect.default_schema_name = db_schema

        context.configure(
            connection=connection, target_metadata=target_metadata,
            include_schemas=True,
        )
        with context.begin_transaction() as trans:
            context.run_migrations()

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

My &#39;event&#39; example below appears to be working but you're right. Your example just seems to rollback when you set echo=True on the engine.

Your example doesn't seem to work in SqlAlchemy&gt;=2 only in SqlAlchemy&lt;2.

After thinking about this it seems if you add
connection.commit() after you set the dialect.default_schema_name = db_schema then your code does seem to work in sqlalchemy 2.

I created a new alembic project, set the sqlalchemy url to a test postgresql db.

Then I:

  1. rm any versions
  2. drop the db
  3. create the db
  4. run alembic revision --autogenerate
  5. run alembic upgrade head

Your version seems to rollback.

I lifted the @event from
setting-alternate-search-paths-on-connect.

from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool
from sqlalchemy.sql import text

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
config.attributes[&#39;schema&#39;] = &#39;myschema&#39;

# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
    fileConfig(config.config_file_name)

# add your model&#39;s MetaData object here
# for &#39;autogenerate&#39; support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from sqlalchemy import MetaData
from sqlalchemy import Table, Column, Integer

metadata = MetaData()
users_t = Table(
    &quot;users&quot;,
    metadata,
    Column(&quot;user_id&quot;, Integer, primary_key=True),
)

target_metadata = metadata

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


def run_migrations_offline() -&gt; None:
    &quot;&quot;&quot;Run migrations in &#39;offline&#39; 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&#39;t even need a DBAPI to be available.

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

    &quot;&quot;&quot;
    url = config.get_main_option(&quot;sqlalchemy.url&quot;)
    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=True,
        dialect_opts={&quot;paramstyle&quot;: &quot;named&quot;},
    )

    with context.begin_transaction():
        context.run_migrations()


def run_migrations_online() -&gt; None:
    &quot;&quot;&quot;Run migrations in &#39;online&#39; mode.

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

    &quot;&quot;&quot;
    connectable = engine_from_config(
        config.get_section(config.config_ini_section, {}),
        prefix=&quot;sqlalchemy.&quot;,
        poolclass=pool.NullPool,
        echo=True,
    )
    db_schema = config.attributes.get(&quot;schema&quot;, &quot;public&quot;)

    # Toggle this between &#39;event&#39; and &#39;inline&#39;
    schema_strategy = &#39;inline&#39;

    if schema_strategy == &#39;event&#39;:
        from sqlalchemy import event
        @event.listens_for(connectable, &quot;connect&quot;, insert=True)
        def set_search_path(dbapi_connection, connection_record):
            existing_autocommit = dbapi_connection.autocommit
            dbapi_connection.autocommit = True
            cursor = dbapi_connection.cursor()
            cursor.execute(f&#39;CREATE SCHEMA IF NOT EXISTS {db_schema}&#39;)
            cursor.execute(f&quot;SET SESSION search_path=&#39;{db_schema}&#39;&quot;)
            cursor.close()
            dbapi_connection.autocommit = existing_autocommit

    with connectable.connect() as connection:

        if schema_strategy == &#39;inline&#39;:
            connection.execute(text(f&#39;CREATE SCHEMA IF NOT EXISTS &quot;{db_schema}&quot;&#39;))
            connection.execute(text(f&quot;SET search_path TO &#39;{db_schema}&#39;&quot;))
            connection.dialect.default_schema_name = db_schema

        context.configure(
            connection=connection, target_metadata=target_metadata,
            include_schemas=True,
        )
        with context.begin_transaction() as trans:
            context.run_migrations()


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

huangapple
  • 本文由 发表于 2023年6月2日 08:21:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76386469.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定