Skip to content

Commit 3d992e7

Browse files
Switch to async DB driver: replace psycopg with asyncpg and update Alembic to async template
1 parent 13652b5 commit 3d992e7

13 files changed

Lines changed: 149 additions & 412 deletions

backend/app/alembic/README

100755100644
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Generic single-database configuration.
1+
Generic single-database configuration with an async dbapi.

backend/app/alembic/env.py

100755100644
Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
1-
import os
1+
import asyncio
22
from logging.config import fileConfig
33

4+
from sqlalchemy import pool
5+
from sqlalchemy.engine import Connection
6+
from sqlalchemy.ext.asyncio import async_engine_from_config
7+
48
from alembic import context
5-
from sqlalchemy import engine_from_config, pool
9+
10+
from app.models import SQLModel # noqa
11+
from app.core.config import settings # noqa
612

713
# this is the Alembic Config object, which provides
814
# access to the values within the .ini file in use.
915
config = context.config
16+
# Override the placeholder URL in alembic.ini with the value from settings.
17+
config.set_main_option("sqlalchemy.url", str(settings.SQLALCHEMY_DATABASE_URI))
1018

1119
# Interpret the config file for Python logging.
1220
# This line sets up loggers basically.
13-
assert config.config_file_name is not None
14-
fileConfig(config.config_file_name)
21+
if config.config_file_name is not None:
22+
fileConfig(config.config_file_name)
1523

1624
# add your model's MetaData object here
1725
# for 'autogenerate' support
1826
# from myapp import mymodel
1927
# target_metadata = mymodel.Base.metadata
20-
# target_metadata = None
21-
22-
from app.models import SQLModel # noqa
23-
from app.core.config import settings # noqa
24-
2528
target_metadata = SQLModel.metadata
2629

2730
# other values from the config, defined by the needs of env.py,
@@ -30,11 +33,7 @@
3033
# ... etc.
3134

3235

33-
def get_url():
34-
return str(settings.SQLALCHEMY_DATABASE_URI)
35-
36-
37-
def run_migrations_offline():
36+
def run_migrations_offline() -> None:
3837
"""Run migrations in 'offline' mode.
3938
4039
This configures the context with just a URL
@@ -46,38 +45,47 @@ def run_migrations_offline():
4645
script output.
4746
4847
"""
49-
url = get_url()
48+
url = config.get_main_option("sqlalchemy.url")
5049
context.configure(
51-
url=url, target_metadata=target_metadata, literal_binds=True, compare_type=True
50+
url=url,
51+
target_metadata=target_metadata,
52+
literal_binds=True,
53+
dialect_opts={"paramstyle": "named"},
5254
)
5355

5456
with context.begin_transaction():
5557
context.run_migrations()
5658

5759

58-
def run_migrations_online():
59-
"""Run migrations in 'online' mode.
60+
def do_run_migrations(connection: Connection) -> None:
61+
context.configure(connection=connection, target_metadata=target_metadata)
62+
63+
with context.begin_transaction():
64+
context.run_migrations()
65+
6066

61-
In this scenario we need to create an Engine
67+
async def run_async_migrations() -> None:
68+
"""In this scenario we need to create an Engine
6269
and associate a connection with the context.
6370
6471
"""
65-
configuration = config.get_section(config.config_ini_section)
66-
assert configuration is not None
67-
configuration["sqlalchemy.url"] = get_url()
68-
connectable = engine_from_config(
69-
configuration,
72+
73+
connectable = async_engine_from_config(
74+
config.get_section(config.config_ini_section, {}),
7075
prefix="sqlalchemy.",
7176
poolclass=pool.NullPool,
7277
)
7378

74-
with connectable.connect() as connection:
75-
context.configure(
76-
connection=connection, target_metadata=target_metadata, compare_type=True
77-
)
79+
async with connectable.connect() as connection:
80+
await connection.run_sync(do_run_migrations)
81+
82+
await connectable.dispose()
83+
84+
85+
def run_migrations_online() -> None:
86+
"""Run migrations in 'online' mode."""
7887

79-
with context.begin_transaction():
80-
context.run_migrations()
88+
asyncio.run(run_async_migrations())
8189

8290

8391
if context.is_offline_mode():

backend/app/alembic/script.py.mako

100755100644
Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,24 @@ Revises: ${down_revision | comma,n}
55
Create Date: ${create_date}
66

77
"""
8+
from typing import Sequence, Union
9+
810
from alembic import op
911
import sqlalchemy as sa
10-
import sqlmodel.sql.sqltypes
1112
${imports if imports else ""}
1213

1314
# revision identifiers, used by Alembic.
14-
revision = ${repr(up_revision)}
15-
down_revision = ${repr(down_revision)}
16-
branch_labels = ${repr(branch_labels)}
17-
depends_on = ${repr(depends_on)}
15+
revision: str = ${repr(up_revision)}
16+
down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
17+
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
18+
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
1819

1920

20-
def upgrade():
21+
def upgrade() -> None:
22+
"""Upgrade schema."""
2123
${upgrades if upgrades else "pass"}
2224

2325

24-
def downgrade():
26+
def downgrade() -> None:
27+
"""Downgrade schema."""
2528
${downgrades if downgrades else "pass"}

backend/app/alembic/versions/.keep

Whitespace-only changes.

backend/app/alembic/versions/1a31ce608336_add_cascade_delete_relationships.py

Lines changed: 0 additions & 37 deletions
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""initialize models
2+
3+
Revision ID: 3452b8e33f88
4+
Revises:
5+
Create Date: 2026-04-28 01:02:57.654652
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = '3452b8e33f88'
16+
down_revision: Union[str, Sequence[str], None] = None
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
"""Upgrade schema."""
23+
pass
24+
25+
26+
def downgrade() -> None:
27+
"""Downgrade schema."""
28+
pass

backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py

Lines changed: 0 additions & 69 deletions
This file was deleted.

backend/app/alembic/versions/d98dd8ec85a3_edit_replace_id_integers_in_all_models_.py

Lines changed: 0 additions & 90 deletions
This file was deleted.

0 commit comments

Comments
 (0)