Skip to content

Commit c39febd

Browse files
committed
✨ Docs: Schema Migrations with Alembic
1 parent 6151f23 commit c39febd

File tree

5 files changed

+207
-0
lines changed

5 files changed

+207
-0
lines changed

docs/advanced/migrations.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Schema Migrations using Alembic
2+
3+
SQLModel integrates with [Alembic](https://alembic.sqlalchemy.org/) to handle schema migrations.
4+
Alembic is a lightweight database migration tool for usage with SQLAlchemy.
5+
Since SQLModel is built on top of SQLAlchemy, it's easy to use Alembic with SQLModel.
6+
7+
## Installation
8+
9+
To use Alembic with SQLModel, first install it:
10+
11+
<div class="termy">
12+
13+
```console
14+
$ pip install alembic
15+
---> 100%
16+
Successfully installed alembic
17+
```
18+
19+
</div>
20+
21+
Then, initialize Alembic in your project directory:
22+
23+
```console
24+
alembic init migrations
25+
```
26+
27+
This will create a directory named `migrations` and a configuration file named `alembic.ini`.
28+
29+
/// info
30+
31+
`migrations` is the directory where Alembic will store the migration scripts.
32+
You can choose any other name for this directory, but `migrations` is a common convention.
33+
34+
///
35+
36+
## Integration
37+
38+
By making `class Table(SQLModel, table=true)`, you can add tables' information to SQLModel(SQLAlchemy) Metadata.
39+
40+
/// info
41+
42+
Metadata is a container object that keeps together many different features of a database.
43+
You can access [Working with Database Metadata](https://docs.sqlalchemy.org/en/20/core/metadata.html) for more information.
44+
45+
///
46+
47+
Import SQLModel on `./migrations/script.py.mako` and add the following code:
48+
49+
```python hl_lines="12"
50+
{!./docs_src/advanced/migrations/tutorial001._py[ln:1-17]!}
51+
52+
# More code here later 👇
53+
```
54+
55+
/// details | 👀 Full file preview
56+
57+
```Python hl_lines="12"
58+
{!./docs_src/advanced/migrations/tutorial001._py!}
59+
```
60+
61+
///
62+
63+
Next, load your models and set the target metadata on `./migrations/env.py`.
64+
65+
```python hl_lines="7 9 24"
66+
{!./docs_src/advanced/migrations/tutorial002._py[ln:1-29]!}
67+
68+
(...)
69+
```
70+
71+
Lastly, set the database connection string in `./alembic.ini`.
72+
73+
```python
74+
# around line 63
75+
sqlalchemy.url = driver://user:pass@localhost/dbname
76+
```
77+
78+
## Revise and Upgrade
79+
80+
After setting up Alembic, you can create a new revision:
81+
82+
```console
83+
alembic revision --autogenerate -m "create table"
84+
```
85+
86+
This will create a new revision file in `./migrations/versions/`.
87+
88+
To apply the new revision and update the database schema, run:
89+
90+
```console
91+
alembic upgrade head
92+
```
93+
94+
/// tip
95+
96+
Remember to run `alembic upgrade head` to update the remote database's schema.
97+
98+
///

docs_src/advanced/migrations/__init__.py

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""${message}
2+
3+
Revision ID: ${up_revision}
4+
Revises: ${down_revision | comma,n}
5+
Create Date: ${create_date}
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
import sqlmodel
13+
${imports if imports else ""}
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = ${repr(up_revision)}
17+
down_revision: Union[str, None] = ${repr(down_revision)}
18+
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
19+
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
20+
21+
22+
def upgrade() -> None:
23+
${upgrades if upgrades else "pass"}
24+
25+
26+
def downgrade() -> None:
27+
${downgrades if downgrades else "pass"}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from logging.config import fileConfig
2+
3+
from sqlalchemy import engine_from_config
4+
from sqlalchemy import pool
5+
6+
from alembic import context
7+
from sqlmodel import SQLModel
8+
9+
from app.models import *
10+
11+
# this is the Alembic Config object, which provides
12+
# access to the values within the .ini file in use.
13+
config = context.config
14+
15+
# Interpret the config file for Python logging.
16+
# This line sets up loggers basically.
17+
if config.config_file_name is not None:
18+
fileConfig(config.config_file_name)
19+
20+
# add your model's MetaData object here
21+
# for 'autogenerate' support
22+
# from myapp import mymodel
23+
# target_metadata = mymodel.Base.metadata
24+
target_metadata = SQLModel.metadata
25+
26+
# other values from the config, defined by the needs of env.py,
27+
# can be acquired:
28+
# my_important_option = config.get_main_option("my_important_option")
29+
# ... etc.
30+
31+
32+
def run_migrations_offline() -> None:
33+
"""Run migrations in 'offline' mode.
34+
35+
This configures the context with just a URL
36+
and not an Engine, though an Engine is acceptable
37+
here as well. By skipping the Engine creation
38+
we don't even need a DBAPI to be available.
39+
40+
Calls to context.execute() here emit the given string to the
41+
script output.
42+
43+
"""
44+
url = config.get_main_option("sqlalchemy.url")
45+
context.configure(
46+
url=url,
47+
target_metadata=target_metadata,
48+
literal_binds=True,
49+
dialect_opts={"paramstyle": "named"},
50+
)
51+
52+
with context.begin_transaction():
53+
context.run_migrations()
54+
55+
56+
def run_migrations_online() -> None:
57+
"""Run migrations in 'online' mode.
58+
59+
In this scenario we need to create an Engine
60+
and associate a connection with the context.
61+
62+
"""
63+
connectable = engine_from_config(
64+
config.get_section(config.config_ini_section, {}),
65+
prefix="sqlalchemy.",
66+
poolclass=pool.NullPool,
67+
)
68+
69+
with connectable.connect() as connection:
70+
context.configure(
71+
connection=connection, target_metadata=target_metadata
72+
)
73+
74+
with context.begin_transaction():
75+
context.run_migrations()
76+
77+
78+
if context.is_offline_mode():
79+
run_migrations_offline()
80+
else:
81+
run_migrations_online()

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ nav:
9999
- Advanced User Guide:
100100
- advanced/index.md
101101
- advanced/decimal.md
102+
- advanced/migrations.md
102103
- alternatives.md
103104
- help.md
104105
- contributing.md

0 commit comments

Comments
 (0)