It's what happens when there's not enough time during the day...

More (Or Less) Alembic CLI

A Follow Up

4 min read Dec 26, 2024 Alembic Advanced Alchemy

More (or Less) Alembic CLI

A Follow Up

Last week, I wrote about Alembic database migrations and how they could be incorporated (and maybe simplified) by usurping the commands into your own CLI. Well, here's a follow up on that.

Recap

In case you missed it, I detailed my desire to incorporate Alembic commands into my own (non-Django) CLI application.

My main gripe within that post is that within the Django ecosystem, the migration story is fairly straightforward. It can be pared down to two simple commands.

But if you're working outside of that framework and have a CLI app that needs some database migration pieces mixed in, you'll need to go through the process of re-acquainting yourself with the Alembic library and API.

After seeing how Advanced Alchemy wraps the alembic commands into their own Click application, I went through the process of doing the same for my own Typer app.

It was a very rewarding exercise in its own right, but I figured it would be best to get this supported out of the box, either with Advanced Alchemy or some other package.

Well, looks like incorporating this functionality directly from advanced-alchemy is now possible.

Click/Typer

When I wrote my original post, I mentioned that the CLI extension in advanced-alchemy was configured to work exclusively with a Litestar application.

During my research, I also noted that there is interplay between Typer and Click (and vice versa), meaning that you can already incorporate a Typer app into Click, or an existing Click command/group into an existing Typer app.

But without being able to access the advanced-alchemy command group, I decided to rewrite it all as a Typer app.

However, it seemed like it could almost work.

Advancing

I shared my original post over on the Litestar Discord, which led to a conversation with one of the maintainers of Advanced Alchemy.

And while we agreed that adding Typer as a dependency to the package (with the solution in my previous post) was not optimal, we did agree that the API for the existing Click command group could be tweaked to make it more accessible (and decoupling it from the Litestar web framework).

After a little back and forth, we found that if you already have the SQLAlchemy and Alembic configuration objects built (by using advanced-alchemy, you probably already have this done), then you really don't need to access the Litestar web application at all.

Initially, the code was improved by adding a add_migration_commands method that would accept an existing Click CLI group and add existing database commands. This was fine for existing Click apps.

But for my Typer app, I needed the Click command group to be exposed to me, so I could then incorporate it into my Typer app.

But that just meant we had to add a return to the add_migration_commands method.

Success

With that little bit of feedback, the maintainer made the change to the main branch of Advanced Alchemy, which means this will likely show up in a near future release.

Instead of re-writing the Click code for Alembic commands into a Typer equivalent, you can call the existing Click group directly from Alembic, and then add it to your app.

It would look something like this:

from advanced_alchemy.cli import add_migration_commands

app = typer.Typer(name="myapp")
click_group = add_migration_commands()
app.add_command(click_group, name="database") # name of option in "myapp"

@app.command()
def hello(name: str):
    print(f"Hello, {name}!")

In the top level commands, you would see:

Commands
db
hello

And if you type myapp db, you will be greeted with the advanced-alchemy database group options:

Commands
downgrade               Downgrade database to a specific revision.
drop-all                Drop all tables from the database.
init                    Initialize migrations for the project.
make-migrations         Make database migrations.
merge-migrations        Merge multiple revisions into a single new revision.
show-current-revision   Shows the current revision for the database.
upgrade                 Upgrade database to a specific revision.

🤩🤩🤩

Amazing!

Caveats

For me, this is great news! I will most definitely use this feature in the future if/when I'm creating a Typer/Click app that needs some quick database migrations/operations (along with other goodies from advanced-alchemy).

If there is any downside to this, it is that the advanced-alchemy commands already occupy their own namespace, and the logic is tucked away within the library, so customization will be limited. (This includes help text and so on).

But on the other hand, that is also an upside in terms of something you don't have to maintain yourself!

And if you really wanted to, you could probably subclass the existing command group and make updates as needed? But at that point, you might as well just rewrite the whole thing like I did in my previous article.

Either way, happy databasing!