Skip to content

Commit a66a925

Browse files
authored
✨ Adopt SQLModel, create models, start using it (fastapi#559)
* 🔥 Remove old SQLAlchemy models * ✨ Add new SQLModel models * 🔧 Update Alembic configs to work with SQLModel * ✨ Re-generate initial Alembic migration * 🔧 Update PostgreSQL driver connection string URL * ✨ Create new SQLModel engine * 🔥 Remove old unneeded SQLAlchemy-specific files * ♻️ Update init_db * ♻️ Use new SQLModel session * ♻️ Update conftest with new DB Session * ♻️ Update pre-start scripts to use SQLModel session * ♻️ Import new SQLModel models * ✨ Create new simplified create_user crud util * ♻️ Update import in CRUDBase class (soon to be removed) * 🙈 Update .gitignore with Python files
1 parent 2d92cd7 commit a66a925

26 files changed

+193
-163
lines changed

src/backend/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
__pycache__
22
app.egg-info
3+
*.pyc

src/backend/app/alembic/env.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
# target_metadata = mymodel.Base.metadata
2121
# target_metadata = None
2222

23-
from app.db.base import Base # noqa
23+
from app.models import SQLModel # noqa
2424

25-
target_metadata = Base.metadata
25+
target_metadata = SQLModel.metadata
2626

2727
# other values from the config, defined by the needs of env.py,
2828
# can be acquired:
@@ -35,7 +35,7 @@ def get_url():
3535
password = os.getenv("POSTGRES_PASSWORD", "")
3636
server = os.getenv("POSTGRES_SERVER", "db")
3737
db = os.getenv("POSTGRES_DB", "app")
38-
return f"postgresql://{user}:{password}@{server}/{db}"
38+
return f"postgresql+psycopg://{user}:{password}@{server}/{db}"
3939

4040

4141
def run_migrations_offline():

src/backend/app/alembic/script.py.mako

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Create Date: ${create_date}
77
"""
88
from alembic import op
99
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
1011
${imports if imports else ""}
1112

1213
# revision identifiers, used by Alembic.

src/backend/app/alembic/versions/d4867f3a4c0a_first_revision.py

Lines changed: 0 additions & 59 deletions
This file was deleted.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Initialize models
2+
3+
Revision ID: e2412789c190
4+
Revises:
5+
Create Date: 2023-11-24 22:55:43.195942
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = 'e2412789c190'
15+
down_revision = None
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.create_table('user',
23+
sa.Column('email', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
24+
sa.Column('is_active', sa.Boolean(), nullable=False),
25+
sa.Column('is_superuser', sa.Boolean(), nullable=False),
26+
sa.Column('full_name', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
27+
sa.Column('id', sa.Integer(), nullable=False),
28+
sa.Column('hashed_password', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
29+
sa.PrimaryKeyConstraint('id')
30+
)
31+
op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True)
32+
op.create_table('item',
33+
sa.Column('description', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
34+
sa.Column('id', sa.Integer(), nullable=False),
35+
sa.Column('title', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
36+
sa.Column('owner_id', sa.Integer(), nullable=False),
37+
sa.ForeignKeyConstraint(['owner_id'], ['user.id'], ),
38+
sa.PrimaryKeyConstraint('id')
39+
)
40+
# ### end Alembic commands ###
41+
42+
43+
def downgrade():
44+
# ### commands auto generated by Alembic - please adjust! ###
45+
op.drop_table('item')
46+
op.drop_index(op.f('ix_user_email'), table_name='user')
47+
op.drop_table('user')
48+
# ### end Alembic commands ###

src/backend/app/app/api/deps.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,16 @@
99
from app import crud, models, schemas
1010
from app.core import security
1111
from app.core.config import settings
12-
from app.db.session import SessionLocal
12+
from app.db.engine import engine
1313

1414
reusable_oauth2 = OAuth2PasswordBearer(
1515
tokenUrl=f"{settings.API_V1_STR}/login/access-token"
1616
)
1717

1818

1919
def get_db() -> Generator:
20-
try:
21-
db = SessionLocal()
22-
yield db
23-
finally:
24-
db.close()
20+
with Session(engine) as session:
21+
yield session
2522

2623

2724
def get_current_user(

src/backend/app/app/backend_pre_start.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import logging
22

3+
from sqlmodel import Session, select
34
from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed
45

5-
from app.db.session import SessionLocal
6+
from app.db.engine import engine
67

78
logging.basicConfig(level=logging.INFO)
89
logger = logging.getLogger(__name__)
@@ -19,9 +20,9 @@
1920
)
2021
def init() -> None:
2122
try:
22-
db = SessionLocal()
23-
# Try to create session to check if DB is awake
24-
db.execute("SELECT 1")
23+
with Session(engine) as session:
24+
# Try to create session to check if DB is awake
25+
session.exec(select(1))
2526
except Exception as e:
2627
logger.error(e)
2728
raise e

src/backend/app/app/celeryworker_pre_start.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import logging
22

3+
from sqlmodel import Session, select
34
from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed
45

5-
from app.db.session import SessionLocal
6+
from app.db.engine import engine
67

78
logging.basicConfig(level=logging.INFO)
89
logger = logging.getLogger(__name__)
@@ -20,8 +21,8 @@
2021
def init() -> None:
2122
try:
2223
# Try to create session to check if DB is awake
23-
db = SessionLocal()
24-
db.execute("SELECT 1")
24+
with Session(engine) as session:
25+
session.exec(select(1))
2526
except Exception as e:
2627
logger.error(e)
2728
raise e

src/backend/app/app/core/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any
4444
if isinstance(v, str):
4545
return v
4646
return PostgresDsn.build(
47-
scheme="postgresql",
47+
scheme="postgresql+psycopg",
4848
user=values.get("POSTGRES_USER"),
4949
password=values.get("POSTGRES_PASSWORD"),
5050
host=values.get("POSTGRES_SERVER"),

src/backend/app/app/crud/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,16 @@
88
# from app.schemas.item import ItemCreate, ItemUpdate
99

1010
# item = CRUDBase[Item, ItemCreate, ItemUpdate](Item)
11+
from sqlmodel import Session
12+
from app.core.security import get_password_hash
13+
from app.models import UserCreate, User
14+
15+
16+
def create_user(session: Session, *, user_create: UserCreate) -> User:
17+
db_obj = User.from_orm(
18+
user_create, update={"hashed_password": get_password_hash(user_create.password)}
19+
)
20+
session.add(db_obj)
21+
session.commit()
22+
session.refresh(db_obj)
23+
return db_obj

0 commit comments

Comments
 (0)