how to set target_metadata programatically ?

338 views
Skip to first unread message

Nikola Radovanovic

unread,
Jan 5, 2021, 10:56:50 AM1/5/21
to sqlalchemy
Hi,
I would like to create set of thin wrappers on top of the alembic since we need some customization. So first thing for me to solve is how to set target_metadata programatically. What I realized by reading documentation and code is that I need something like:

alembic_cfg = Config("alembic.ini")
alembic_cfg.set_main_option("sqlalchemy.url", "postgres://user:pass@localhost/testdb")
script = ScriptDirectory.from_config(alembic_cfg)

engine = create_engine(config.db_uri)
with engine.connect() as connection:
    env_ctx = EnvironmentContext(config=alembic_cfg, script=script)
    env_ctx.configure(connection=connection, target_metadata=Base.metadata)
    ctx = env_ctx.get_context()

Next, I want to call revision with autogenerate set to True, but I am not sure how?

Thank you in advance.

Kindest regards

Mike Bayer

unread,
Jan 5, 2021, 1:21:08 PM1/5/21
to noreply-spamdigest via sqlalchemy
hi there -

usually programmatic customization of Alembic is done via the env.py script directly, that is, you would run "alembic revision" normally and it's within your local env.py that you can control how target_metadata is achieved.  you can also pass custom options to "alembic revision" using the -x argument : https://alembic.sqlalchemy.org/en/latest/api/runtime.html#alembic.runtime.environment.EnvironmentContext.get_x_argument .   (call this option one)

all of that said, what you are describing is in fact a custom front-end to Alembic.    These are also commonplace, and in these cases people usually call the command functions directly, as in the examples at https://alembic.sqlalchemy.org/en/latest/api/commands.html .

In this latter case, you can pass custom arguments and/or your target_metadata into the command using the config.attributes dictionary.  but again, you would need to have your env.py script coordinate with this state so that your front end creates an alembic.Config(), puts things inside of its .attributes, runs command.revision(config), then your env.py pulls data out of config.attributes.   (Call this option two).

Finally, the third option where you really don't want env.py to be involved at all and you want to directly invoke "revision with autogenerate" is to use the autogenerate API directly.   This is more work to implement and has more chance of there being backwards incompatible changes at a future date, but you could use the API as demonstrated in the source code to command.revision itself: https://github.com/sqlalchemy/alembic/blob/master/alembic/command.py#L159

I would advise sticking with option one or option two, however feel free to experiment with any one of them to learn the general flow of things.
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
 
 
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description.
---
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+...@googlegroups.com.

Nikola Radovanovic

unread,
Jan 6, 2021, 1:43:25 AM1/6/21
to sqlalchemy
Thanks.
Maybe it would be better that I explain the whole scenario so everyone reading can have better overview of what I want to achieve.

We have some "general" tables (like users, permissions, roles and similar) that are referenced from multi-tenant client schemes ("client_1", "client_2", etc). Atm, we have some simple "migration" Python script that runs given *.sql migration file on general and all client schemes; so we are considering using a better tool (Alembic) to do this for us, but we have to customize it (not yet sure how exactly).

Second issue is polymorphic related. We are using plain IntEnum to store "discriminator" field. During alembic migration, this is imported as Python IntEnum, but I would like it to become plain sa.Integer().

Third one is the originally posted. I guess it would be easier to change env.py programatically, or use some of the techniques mentioned in previous response. It seems that I will have to use target_metadata on another places anyway, so just wanted to see how to actually do this. I can debug (and probably will) how Alembic itself handles context creation and calls commands, but was not sure is this the right path or there are better ones.

(We use declarative_base - I forgot to mention that)

Thank you all.

Kindest regards

Mike Bayer

unread,
Jan 6, 2021, 8:29:57 AM1/6/21
to noreply-spamdigest via sqlalchemy
the reason Alembic puts env.py into your project, rather than having this just be part of Alembic, is so that you can customize it as needed, so it sounds like for everything you're doing you want to be inside of env.py making those changes.
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
 
 
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description.
---
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages