This article explains setting up Alembic for migration management with SQLAlchemy in a FastAPI project.
This article explains how to set up Alembic for migration management with SQLAlchemy, specifically within a FastAPI project. You will learn how to install, initialize, and configure Alembic to keep track of your database schema changes.
Begin by installing Alembic using pip. This not only installs the package but also provides access to its command-line interface:
Copy
Ask AI
pip install alembic
After installing, verify the installation by running:
Copy
Ask AI
alembic --help
You should see output similar to this:
Copy
Ask AI
Requirement already satisfied: six==1.5 in c:\users\sanje\documents\courses\fastapi\venv\lib\site-packagesInstalling collected packages: alembicSuccessfully installed alembic-1.6.5WARNING: You are using pip version 21.2.1; however, version 21.2.4 is available.You should consider upgrading via the 'c:\users\sanje\documents\courses\fastapi\venv\scripts\python.exe -m pip install --upgrade pip' command.
Before moving forward, note that the sample code below presents snippets of your SQLAlchemy models. For instance, you could have models for User and Vote as follows:
Before running migrations, ensure that your application is stopped to prevent automatic restarts during the migration process. If required, drop any existing tables—using DROP CASCADE if there are foreign key constraints—to start with a clean slate.To check the current data in your database, you can run the following SQL query:
Initialize Alembic to create the necessary directory structure and configuration files. The Alembic CLI provides several subcommands, which you can review using:
Copy
Ask AI
positional arguments: {branches,current,downgrade,edit,heads,history,init,list_templates,merge,revision,show,stamp,upgrade} branches Show current branch points. current Display the current revision for a database. downgrade Revert to a previous version. edit Edit revision script(s) using $EDITOR. heads Show current available heads in the script directory. history List changed scripts in chronological order. init Initialize a new scripts directory. list_templates List available templates. merge Merge two revisions together. Creates a new migration file. revision Create a new revision file. show Show the revision(s) denoted by the given symbol. stamp "stamp" the revision table with the given revision; don't run any migrations. upgrade Upgrade to a later version.optional arguments: -h, --help show this help message and exit --version show program's version number and exit -C CONFIG, --config CONFIG Alternate config file; defaults to value of ALEMBIC_CONFIG environment -n NAME, --name NAME Name of section in ini file to use for Alembic config -x X Additional arguments consumed by custom env.py scripts, e.g. -x setting1=something -x setting2=something -r, --raiseerr Raise a full stack trace on error
You can also check help for initialization by running:
This creates the Alembic directory outside the application folder along with the alembic.ini configuration file. Next, you need to update both the env.py and alembic.ini files to connect Alembic with your SQLAlchemy models and database settings.
The env.py file is the main configuration file for Alembic. It must be updated to import your SQLAlchemy models and set the target metadata for autogeneration. An initial snippet might look like this:
Copy
Ask AI
from logging.config import fileConfigfrom sqlalchemy import engine_from_configfrom sqlalchemy import poolfrom alembic import context# 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)# add your model's MetaData object here# for 'autogenerate' supportfrom myapp import mymodeltarget_metadata = mymodel.Base.metadatatarget_metadata = None
To work with SQLAlchemy models, update the file to import your base object from your database module. For example:
Then, modify your env.py file to import Base from your application’s database file. Replace the original metadata configuration with:
Copy
Ask AI
from logging.config import fileConfigfrom sqlalchemy import engine_from_config, poolfrom alembic import contextfrom app.database import Basefrom app.config import settings# this is the Alembic Config object, which provides# access to the values within the .ini file in use.config = context.configconfig.set_main_option( "sqlalchemy.url", f"postgresql+psycopg2://{settings.database_username}:{settings.database_password}@{settings.database_hostname}:{settings.database_port}/{settings.database_name}")# Interpret the config file for Python logging.fileConfig(config.config_file_name)# add your model's MetaData object here for 'autogenerate' supporttarget_metadata = Base.metadata
If your models are defined or imported from a different file (e.g., app.models), ensure the import reflects that. For instance, to detect changes in a Post model:
Copy
Ask AI
from sqlalchemy import Column, Integer, String, Boolean, ForeignKeyfrom sqlalchemy.orm import relationshipfrom sqlalchemy.sql.expression import textfrom sqlalchemy.sql.sqltypes import TIMESTAMPfrom app.database import Baseclass Post(Base): __tablename__ = "posts" id = Column(Integer, primary_key=True, nullable=False) title = Column(String, nullable=False) content = Column(String, nullable=False) published = Column(Boolean, server_default='TRUE', nullable=False) created_at = Column(TIMESTAMP(timezone=True), nullable=False, server_default=text('now()')) owner_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) owner = relationship("User")class User(Base): __tablename__ = "users" # Define additional columns for the User model here.
Then, update your env.py accordingly:
Copy
Ask AI
from logging.config import fileConfigfrom sqlalchemy import engine_from_config, poolfrom alembic import contextfrom app.models import Basefrom app.config import settings# this is the Alembic Config object, which provides# access to the values within the .ini file in use.config = context.configconfig.set_main_option( "sqlalchemy.url", f"postgresql+psycopg2://{settings.database_username}:{settings.database_password}@{settings.database_hostname}:{settings.database_port}/{settings.database_name}")# Interpret the config file for Python logging.fileConfig(config.config_file_name)# add your model's MetaData object here for 'autogenerate' supporttarget_metadata = Base.metadata
This configuration ensures that Alembic can access your SQLAlchemy models and automatically track schema changes.
Within the alembic.ini file, specify your SQLAlchemy database URL. Initially, it might appear as follows:
Copy
Ask AI
[alembic]# path to migration scriptsscript_location = alembic# template used to generate migration files# sys.path path, will be prepended to sys.path if present.# defaults to the current working directory.prepend_sys_path = .# 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# max length of characters to apply to thesqlalchemy.url = driver://user:pass@localhost/dbname[post_write_hooks]# post_write_hooks defines scripts or Python functions that are run# on newly generated revision scripts. See the documentation for further# detail and examples.
Replace the placeholder connection string with your actual database credentials. For example, if you are using PostgreSQL:
While the above example hardcodes credentials for demonstration purposes, it is advisable to manage sensitive information using environment variables in production.
To avoid hardcoding credentials within alembic.ini, you can override the SQLAlchemy URL directly in env.py using values from your configuration file. For example:
Copy
Ask AI
from alembic import contextfrom app.database import Basefrom app.config import settingsfrom logging.config import fileConfigfrom sqlalchemy import engine_from_config, pool# this is the Alembic Config object, which provides access to the values within the .ini file in use.config = context.configconfig.set_main_option( "sqlalchemy.url", f"postgresql+psycopg2://{settings.database_username}:{settings.database_password}@{settings.database_hostname}:{settings.database_port}/{settings.database_name}")# Interpret the config file for Python logging.fileConfig(config.config_file_name)# add your model's MetaData object here for 'autogenerate' supporttarget_metadata = Base.metadata
This method allows you to securely manage database credentials using environment variables. A typical Pydantic settings class defined in config.py might resemble:
Copy
Ask AI
from pydantic import BaseSettingsclass Settings(BaseSettings): database_hostname: str database_port: str database_password: str database_name: str database_username: str secret_key: str algorithm: str access_token_expire_minutes: int class Config: env_file = ".env"
You can then use these settings in your main application as follows:
After completing these configurations, Alembic will be connected to your PostgreSQL database and ready to detect changes in your SQLAlchemy models. This setup streamlines the process of generating migration scripts and applying database updates.To create and run migrations, use the following commands:
Copy
Ask AI
alembic revision --autogenerate -m "Your migration message"alembic upgrade head