Drop Sqlmodel and use plain Sqlalchemy

This commit is contained in:
2025-10-11 22:14:04 +02:00
parent a190471b44
commit 494170e2ab
4 changed files with 93 additions and 93 deletions

View File

@@ -8,9 +8,10 @@ from fastapi import Depends, FastAPI, Request
from fastapi.responses import RedirectResponse from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from sqlmodel import Session, SQLModel, create_engine, select from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session
from models import Event, Household, Registration, Subscription, TeamRegistration from models import Base, Event, Household, Registration, Subscription, TeamRegistration
sqlite_file_name = "database.db" sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}" sqlite_url = f"sqlite:///{sqlite_file_name}"
@@ -27,7 +28,7 @@ def get_session():
def create_db_and_tables(): def create_db_and_tables():
SQLModel.metadata.create_all(engine) Base.metadata.create_all(engine)
@asynccontextmanager @asynccontextmanager
@@ -54,7 +55,7 @@ async def index(request: Request, session: SessionDep):
.order_by(Event.event_time) .order_by(Event.event_time)
.where(Event.event_time >= now - timedelta(days=1)) .where(Event.event_time >= now - timedelta(days=1))
) )
events = session.exec(statement).all() events = session.scalars(statement)
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="index.html", name="index.html",
@@ -71,7 +72,7 @@ async def past_events(request: Request, session: SessionDep):
.order_by(Event.event_time) .order_by(Event.event_time)
.where(Event.event_time < now - timedelta(days=1)) .where(Event.event_time < now - timedelta(days=1))
) )
events = session.exec(statement).all() events = session.scalars(statement)
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="index.html", name="index.html",
@@ -82,9 +83,9 @@ async def past_events(request: Request, session: SessionDep):
@app.get("/subscribe") @app.get("/subscribe")
async def subscribe(request: Request, session: SessionDep): async def subscribe(request: Request, session: SessionDep):
statement = select(Household) statement = select(Household)
households = session.exec(statement).all() households = session.scalars(statement)
subscriptions = session.exec(select(Subscription)).all() subscriptions = session.scalars(select(Subscription))
# filter out households with existing subscriptions # filter out households with existing subscriptions
households = [ households = [
@@ -138,7 +139,7 @@ async def add_subscribe(request: Request, session: SessionDep):
async def delete_subscription(request: Request, session: SessionDep, household_id: int): async def delete_subscription(request: Request, session: SessionDep, household_id: int):
statement = select(Subscription).where(Subscription.household_id == household_id) statement = select(Subscription).where(Subscription.household_id == household_id)
sub = session.exec(statement).one() sub = session.scalars(statement).one()
session.delete(sub) session.delete(sub)
session.commit() session.commit()
@@ -183,10 +184,10 @@ async def add_event(request: Request, session: SessionDep):
@app.get("/event/{event_id}") @app.get("/event/{event_id}")
async def read_event(request: Request, event_id: int, session: SessionDep): async def read_event(request: Request, event_id: int, session: SessionDep):
statement = select(Event).where(Event.id == event_id) statement = select(Event).where(Event.id == event_id)
event = session.exec(statement).one() event = session.scalars(statement).one()
statement = select(Household) statement = select(Household)
households = session.exec(statement).all() households = session.scalars(statement)
# filter out households with existing registrations # filter out households with existing registrations
households = [ households = [
@@ -240,7 +241,7 @@ async def delete_registration(
statement = select(Registration).where( statement = select(Registration).where(
Registration.household_id == household_id, Registration.event_id == event_id Registration.household_id == household_id, Registration.event_id == event_id
) )
session.delete(session.exec(statement).one()) session.delete(session.scalars(statement).one())
session.commit() session.commit()
return RedirectResponse(url=f"/event/{event_id}", status_code=status.HTTP_302_FOUND) return RedirectResponse(url=f"/event/{event_id}", status_code=status.HTTP_302_FOUND)
@@ -256,13 +257,12 @@ async def add_team_registration(request: Request, event_id: int, session: Sessio
TeamRegistration.person_name == person, TeamRegistration.work_type == work_type TeamRegistration.person_name == person, TeamRegistration.work_type == work_type
) )
# if the person has already registered for the same work type, just ignore # if the person has already registered for the same work type, just ignore
if session.exec(statement).one_or_none() is None: if session.scalars(statement).one_or_none() is None:
registration = TeamRegistration( registration = TeamRegistration(
person_name=person, person_name=person,
event_id=event_id, event_id=event_id,
work_type=form_data["workType"], work_type=form_data["workType"],
) )
TeamRegistration.model_validate(registration)
session.add(registration) session.add(registration)
session.commit() session.commit()
return RedirectResponse(url=f"/event/{event_id}", status_code=status.HTTP_302_FOUND) return RedirectResponse(url=f"/event/{event_id}", status_code=status.HTTP_302_FOUND)
@@ -276,6 +276,6 @@ async def delete_team_registration(
session: SessionDep, session: SessionDep,
): ):
statement = select(TeamRegistration).where(TeamRegistration.id == entry_id) statement = select(TeamRegistration).where(TeamRegistration.id == entry_id)
session.delete(session.exec(statement).one()) session.delete(session.scalars(statement).one())
session.commit() session.commit()
return RedirectResponse(url=f"/event/{event_id}", status_code=status.HTTP_302_FOUND) return RedirectResponse(url=f"/event/{event_id}", status_code=status.HTTP_302_FOUND)

View File

@@ -1,33 +1,40 @@
import typing import typing
from datetime import datetime from datetime import datetime
from xmlrpc.client import DateTime
from sqlmodel import Field, Relationship, SQLModel, String from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy.types import Text
class Base(DeclarativeBase):
pass
WorkTypes = typing.Literal["cooking", "dishes", "tables"] WorkTypes = typing.Literal["cooking", "dishes", "tables"]
class Event(SQLModel, table=True): class Event(Base):
id: int | None = Field(default=None, primary_key=True) __tablename__ = "event"
title: str = Field(nullable=False) id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
event_time: datetime = Field(nullable=False) title: Mapped[str] = mapped_column(nullable=False)
registration_deadline: datetime = Field(nullable=False) event_time: Mapped[datetime] = mapped_column(nullable=False)
description: str registration_deadline: Mapped[datetime] = mapped_column(nullable=False)
recipe_link: str description: Mapped[str] = mapped_column()
recipe_link: Mapped[str] = mapped_column()
# Min and max number of people needed for cooking, doing the dishes and preparing the tables # Min and max number of people needed for cooking, doing the dishes and preparing the tables
team_cooking_min: int = 3 team_cooking_min: Mapped[int] = mapped_column(default=3, nullable=False)
team_cooking_max: int = 5 team_cooking_max: Mapped[int] = mapped_column(default=5, nullable=False)
team_dishes_min: int = 3 team_dishes_min: Mapped[int] = mapped_column(default=3, nullable=False)
team_dishes_max: int = 5 team_dishes_max: Mapped[int] = mapped_column(default=5, nullable=False)
# Todo: Rename to "table" # Todo: Rename to "table"
team_prep_min: int = 1 team_prep_min: Mapped[int] = mapped_column(default=1, nullable=False)
team_prep_max: int = 1 team_prep_max: Mapped[int] = mapped_column(default=1, nullable=False)
registrations: list["Registration"] = Relationship() registrations: Mapped[list["Registration"]] = relationship("Registration")
team: list["TeamRegistration"] = Relationship() team: Mapped[list["TeamRegistration"]] = relationship("TeamRegistration")
def team_min_reached(self, work_type: WorkTypes): def team_min_reached(self, work_type: WorkTypes):
threshold = { threshold = {
@@ -56,52 +63,58 @@ class Event(SQLModel, table=True):
) )
class TeamRegistration(SQLModel, table=True): class TeamRegistration(Base):
id: int | None = Field(default=None, primary_key=True) __tablename__ = "team_registration"
event_id: int | None = Field(default=None, foreign_key="event.id") id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
person_name: str = Field(nullable=False) event_id: Mapped[int] = mapped_column(ForeignKey("event.id"))
work_type: WorkTypes = Field(nullable=False, sa_type=String) person_name: Mapped[str] = mapped_column(nullable=False)
comment: str | None work_type: Mapped[WorkTypes] = mapped_column(Text, nullable=False)
comment: Mapped[str | None] = mapped_column()
class Household(SQLModel, table=True): class Household(Base):
id: int | None = Field(default=None, primary_key=True) __tablename__ = "household"
name: str = Field(nullable=False) id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(nullable=False)
class Registration(SQLModel, table=True): class Registration(Base):
event_id: int | None = Field(default=None, foreign_key="event.id", primary_key=True) __tablename__ = "registration"
household_id: int | None = Field( event_id: Mapped[int] = mapped_column(ForeignKey("event.id"), primary_key=True)
default=None, foreign_key="household.id", primary_key=True household_id: Mapped[int] = mapped_column(
ForeignKey("household.id"), primary_key=True
) )
num_adult_meals: int num_adult_meals: Mapped[int] = mapped_column(nullable=False)
num_children_meals: int num_children_meals: Mapped[int] = mapped_column(nullable=False)
num_small_children_meals: int num_small_children_meals: Mapped[int] = mapped_column(nullable=False)
comment: str | None comment: Mapped[str | None] = mapped_column()
household: Household = Relationship() household: Mapped["Household"] = relationship()
class Subscription(SQLModel, table=True): class Subscription(Base):
household_id: int | None = Field( __tablename__ = "subscription"
default=None, foreign_key="household.id", primary_key=True household_id: Mapped[int] = mapped_column(
ForeignKey("household.id"), primary_key=True
) )
num_adult_meals: int num_adult_meals: Mapped[int] = mapped_column(nullable=False)
num_children_meals: int num_children_meals: Mapped[int] = mapped_column(nullable=False)
num_small_children_meals: int num_small_children_meals: Mapped[int] = mapped_column(nullable=False)
comment: str | None comment: Mapped[str | None] = mapped_column()
last_modified: datetime = Field(default_factory=datetime.now, nullable=False) last_modified: Mapped[datetime] = mapped_column(
default=datetime.now, nullable=False
)
monday: bool = True monday: Mapped[bool] = mapped_column(default=True, nullable=False)
tuesday: bool = True tuesday: Mapped[bool] = mapped_column(default=True, nullable=False)
wednesday: bool = True wednesday: Mapped[bool] = mapped_column(default=True, nullable=False)
thursday: bool = True thursday: Mapped[bool] = mapped_column(default=True, nullable=False)
friday: bool = True friday: Mapped[bool] = mapped_column(default=True, nullable=False)
saturday: bool = True saturday: Mapped[bool] = mapped_column(default=True, nullable=False)
sunday: bool = True sunday: Mapped[bool] = mapped_column(default=True, nullable=False)
household: Household = Relationship() household: Mapped["Household"] = relationship()
def day_string_de(self) -> str: def day_string_de(self) -> str:
""" """

View File

@@ -6,7 +6,7 @@ readme = "README.md"
requires-python = "~=3.13.0" requires-python = "~=3.13.0"
dependencies = [ dependencies = [
"fastapi[standard]>=0.116.0", "fastapi[standard]>=0.116.0",
"sqlmodel>=0.0.24", "sqlalchemy>=2.0.44",
"uvicorn[standard]>=0.35.0", "uvicorn[standard]>=0.35.0",
] ]

View File

@@ -321,7 +321,7 @@ version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "fastapi", extra = ["standard"] }, { name = "fastapi", extra = ["standard"] },
{ name = "sqlmodel" }, { name = "sqlalchemy" },
{ name = "uvicorn", extra = ["standard"] }, { name = "uvicorn", extra = ["standard"] },
] ]
@@ -334,7 +334,7 @@ dev = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "fastapi", extras = ["standard"], specifier = ">=0.116.0" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.116.0" },
{ name = "sqlmodel", specifier = ">=0.0.24" }, { name = "sqlalchemy", specifier = ">=2.0.44" },
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.35.0" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.35.0" },
] ]
@@ -553,36 +553,23 @@ wheels = [
[[package]] [[package]]
name = "sqlalchemy" name = "sqlalchemy"
version = "2.0.43" version = "2.0.44"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/d7/bc/d59b5d97d27229b0e009bd9098cd81af71c2fa5549c580a0a67b9bed0496/sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417", size = 9762949, upload-time = "2025-08-11T14:24:58.438Z" } sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830, upload-time = "2025-10-10T14:39:12.935Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/41/1c/a7260bd47a6fae7e03768bf66451437b36451143f36b285522b865987ced/sqlalchemy-2.0.43-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e7c08f57f75a2bb62d7ee80a89686a5e5669f199235c6d1dac75cd59374091c3", size = 2130598, upload-time = "2025-08-11T15:51:15.903Z" }, { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479, upload-time = "2025-10-10T16:03:37.671Z" },
{ url = "https://files.pythonhosted.org/packages/8e/84/8a337454e82388283830b3586ad7847aa9c76fdd4f1df09cdd1f94591873/sqlalchemy-2.0.43-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14111d22c29efad445cd5021a70a8b42f7d9152d8ba7f73304c4d82460946aaa", size = 2118415, upload-time = "2025-08-11T15:51:17.256Z" }, { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212, upload-time = "2025-10-10T16:03:41.755Z" },
{ url = "https://files.pythonhosted.org/packages/cf/ff/22ab2328148492c4d71899d62a0e65370ea66c877aea017a244a35733685/sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21b27b56eb2f82653168cefe6cb8e970cdaf4f3a6cb2c5e3c3c1cf3158968ff9", size = 3248707, upload-time = "2025-08-11T15:52:38.444Z" }, { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353, upload-time = "2025-10-10T15:35:31.221Z" },
{ url = "https://files.pythonhosted.org/packages/dc/29/11ae2c2b981de60187f7cbc84277d9d21f101093d1b2e945c63774477aba/sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5a9da957c56e43d72126a3f5845603da00e0293720b03bde0aacffcf2dc04f", size = 3253602, upload-time = "2025-08-11T15:56:37.348Z" }, { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222, upload-time = "2025-10-10T15:43:50.124Z" },
{ url = "https://files.pythonhosted.org/packages/b8/61/987b6c23b12c56d2be451bc70900f67dd7d989d52b1ee64f239cf19aec69/sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d79f9fdc9584ec83d1b3c75e9f4595c49017f5594fee1a2217117647225d738", size = 3183248, upload-time = "2025-08-11T15:52:39.865Z" }, { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614, upload-time = "2025-10-10T15:35:32.578Z" },
{ url = "https://files.pythonhosted.org/packages/86/85/29d216002d4593c2ce1c0ec2cec46dda77bfbcd221e24caa6e85eff53d89/sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9df7126fd9db49e3a5a3999442cc67e9ee8971f3cb9644250107d7296cb2a164", size = 3219363, upload-time = "2025-08-11T15:56:39.11Z" }, { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248, upload-time = "2025-10-10T15:43:51.862Z" },
{ url = "https://files.pythonhosted.org/packages/b6/e4/bd78b01919c524f190b4905d47e7630bf4130b9f48fd971ae1c6225b6f6a/sqlalchemy-2.0.43-cp313-cp313-win32.whl", hash = "sha256:7f1ac7828857fcedb0361b48b9ac4821469f7694089d15550bbcf9ab22564a1d", size = 2096718, upload-time = "2025-08-11T15:55:05.349Z" }, { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275, upload-time = "2025-10-10T15:03:26.096Z" },
{ url = "https://files.pythonhosted.org/packages/ac/a5/ca2f07a2a201f9497de1928f787926613db6307992fe5cda97624eb07c2f/sqlalchemy-2.0.43-cp313-cp313-win_amd64.whl", hash = "sha256:971ba928fcde01869361f504fcff3b7143b47d30de188b11c6357c0505824197", size = 2123200, upload-time = "2025-08-11T15:55:07.932Z" }, { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901, upload-time = "2025-10-10T15:03:27.548Z" },
{ url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" }, { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" },
]
[[package]]
name = "sqlmodel"
version = "0.0.24"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
{ name = "sqlalchemy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/86/4b/c2ad0496f5bdc6073d9b4cef52be9c04f2b37a5773441cc6600b1857648b/sqlmodel-0.0.24.tar.gz", hash = "sha256:cc5c7613c1a5533c9c7867e1aab2fd489a76c9e8a061984da11b4e613c182423", size = 116780, upload-time = "2025-03-07T05:43:32.887Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/16/91/484cd2d05569892b7fef7f5ceab3bc89fb0f8a8c0cde1030d383dbc5449c/sqlmodel-0.0.24-py3-none-any.whl", hash = "sha256:6778852f09370908985b667d6a3ab92910d0d5ec88adcaf23dbc242715ff7193", size = 28622, upload-time = "2025-03-07T05:43:30.37Z" },
] ]
[[package]] [[package]]