diff --git a/new-registration-app/main.py b/new-registration-app/main.py index c3bd015..76cda38 100644 --- a/new-registration-app/main.py +++ b/new-registration-app/main.py @@ -8,9 +8,10 @@ from fastapi import Depends, FastAPI, Request from fastapi.responses import RedirectResponse from fastapi.staticfiles import StaticFiles 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_url = f"sqlite:///{sqlite_file_name}" @@ -27,7 +28,7 @@ def get_session(): def create_db_and_tables(): - SQLModel.metadata.create_all(engine) + Base.metadata.create_all(engine) @asynccontextmanager @@ -54,7 +55,7 @@ async def index(request: Request, session: SessionDep): .order_by(Event.event_time) .where(Event.event_time >= now - timedelta(days=1)) ) - events = session.exec(statement).all() + events = session.scalars(statement) return templates.TemplateResponse( request=request, name="index.html", @@ -71,7 +72,7 @@ async def past_events(request: Request, session: SessionDep): .order_by(Event.event_time) .where(Event.event_time < now - timedelta(days=1)) ) - events = session.exec(statement).all() + events = session.scalars(statement) return templates.TemplateResponse( request=request, name="index.html", @@ -82,9 +83,9 @@ async def past_events(request: Request, session: SessionDep): @app.get("/subscribe") async def subscribe(request: Request, session: SessionDep): 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 households = [ @@ -138,7 +139,7 @@ async def add_subscribe(request: Request, session: SessionDep): async def delete_subscription(request: Request, session: SessionDep, household_id: int): statement = select(Subscription).where(Subscription.household_id == household_id) - sub = session.exec(statement).one() + sub = session.scalars(statement).one() session.delete(sub) session.commit() @@ -183,10 +184,10 @@ async def add_event(request: Request, session: SessionDep): @app.get("/event/{event_id}") async def read_event(request: Request, event_id: int, session: SessionDep): statement = select(Event).where(Event.id == event_id) - event = session.exec(statement).one() + event = session.scalars(statement).one() statement = select(Household) - households = session.exec(statement).all() + households = session.scalars(statement) # filter out households with existing registrations households = [ @@ -240,7 +241,7 @@ async def delete_registration( statement = select(Registration).where( Registration.household_id == household_id, Registration.event_id == event_id ) - session.delete(session.exec(statement).one()) + session.delete(session.scalars(statement).one()) session.commit() 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 ) # 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( person_name=person, event_id=event_id, work_type=form_data["workType"], ) - TeamRegistration.model_validate(registration) session.add(registration) session.commit() return RedirectResponse(url=f"/event/{event_id}", status_code=status.HTTP_302_FOUND) @@ -276,6 +276,6 @@ async def delete_team_registration( session: SessionDep, ): statement = select(TeamRegistration).where(TeamRegistration.id == entry_id) - session.delete(session.exec(statement).one()) + session.delete(session.scalars(statement).one()) session.commit() return RedirectResponse(url=f"/event/{event_id}", status_code=status.HTTP_302_FOUND) diff --git a/new-registration-app/models.py b/new-registration-app/models.py index 019f2be..a43b0b2 100644 --- a/new-registration-app/models.py +++ b/new-registration-app/models.py @@ -1,33 +1,40 @@ import typing 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"] -class Event(SQLModel, table=True): - id: int | None = Field(default=None, primary_key=True) - title: str = Field(nullable=False) - event_time: datetime = Field(nullable=False) - registration_deadline: datetime = Field(nullable=False) - description: str - recipe_link: str +class Event(Base): + __tablename__ = "event" + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + title: Mapped[str] = mapped_column(nullable=False) + event_time: Mapped[datetime] = mapped_column(nullable=False) + registration_deadline: Mapped[datetime] = mapped_column(nullable=False) + 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 - team_cooking_min: int = 3 - team_cooking_max: int = 5 + team_cooking_min: Mapped[int] = mapped_column(default=3, nullable=False) + team_cooking_max: Mapped[int] = mapped_column(default=5, nullable=False) - team_dishes_min: int = 3 - team_dishes_max: int = 5 + team_dishes_min: Mapped[int] = mapped_column(default=3, nullable=False) + team_dishes_max: Mapped[int] = mapped_column(default=5, nullable=False) # Todo: Rename to "table" - team_prep_min: int = 1 - team_prep_max: int = 1 + team_prep_min: Mapped[int] = mapped_column(default=1, nullable=False) + team_prep_max: Mapped[int] = mapped_column(default=1, nullable=False) - registrations: list["Registration"] = Relationship() - team: list["TeamRegistration"] = Relationship() + registrations: Mapped[list["Registration"]] = relationship("Registration") + team: Mapped[list["TeamRegistration"]] = relationship("TeamRegistration") def team_min_reached(self, work_type: WorkTypes): threshold = { @@ -56,52 +63,58 @@ class Event(SQLModel, table=True): ) -class TeamRegistration(SQLModel, table=True): - id: int | None = Field(default=None, primary_key=True) - event_id: int | None = Field(default=None, foreign_key="event.id") - person_name: str = Field(nullable=False) - work_type: WorkTypes = Field(nullable=False, sa_type=String) - comment: str | None +class TeamRegistration(Base): + __tablename__ = "team_registration" + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + event_id: Mapped[int] = mapped_column(ForeignKey("event.id")) + person_name: Mapped[str] = mapped_column(nullable=False) + work_type: Mapped[WorkTypes] = mapped_column(Text, nullable=False) + comment: Mapped[str | None] = mapped_column() -class Household(SQLModel, table=True): - id: int | None = Field(default=None, primary_key=True) - name: str = Field(nullable=False) +class Household(Base): + __tablename__ = "household" + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + name: Mapped[str] = mapped_column(nullable=False) -class Registration(SQLModel, table=True): - event_id: int | None = Field(default=None, foreign_key="event.id", primary_key=True) - household_id: int | None = Field( - default=None, foreign_key="household.id", primary_key=True +class Registration(Base): + __tablename__ = "registration" + event_id: Mapped[int] = mapped_column(ForeignKey("event.id"), primary_key=True) + household_id: Mapped[int] = mapped_column( + ForeignKey("household.id"), primary_key=True ) - num_adult_meals: int - num_children_meals: int - num_small_children_meals: int - comment: str | None + num_adult_meals: Mapped[int] = mapped_column(nullable=False) + num_children_meals: Mapped[int] = mapped_column(nullable=False) + num_small_children_meals: Mapped[int] = mapped_column(nullable=False) + comment: Mapped[str | None] = mapped_column() - household: Household = Relationship() + household: Mapped["Household"] = relationship() -class Subscription(SQLModel, table=True): - household_id: int | None = Field( - default=None, foreign_key="household.id", primary_key=True +class Subscription(Base): + __tablename__ = "subscription" + household_id: Mapped[int] = mapped_column( + ForeignKey("household.id"), primary_key=True ) - num_adult_meals: int - num_children_meals: int - num_small_children_meals: int - comment: str | None + num_adult_meals: Mapped[int] = mapped_column(nullable=False) + num_children_meals: Mapped[int] = mapped_column(nullable=False) + num_small_children_meals: Mapped[int] = mapped_column(nullable=False) + 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 - tuesday: bool = True - wednesday: bool = True - thursday: bool = True - friday: bool = True - saturday: bool = True - sunday: bool = True + monday: Mapped[bool] = mapped_column(default=True, nullable=False) + tuesday: Mapped[bool] = mapped_column(default=True, nullable=False) + wednesday: Mapped[bool] = mapped_column(default=True, nullable=False) + thursday: Mapped[bool] = mapped_column(default=True, nullable=False) + friday: Mapped[bool] = mapped_column(default=True, nullable=False) + saturday: Mapped[bool] = mapped_column(default=True, nullable=False) + sunday: Mapped[bool] = mapped_column(default=True, nullable=False) - household: Household = Relationship() + household: Mapped["Household"] = relationship() def day_string_de(self) -> str: """ diff --git a/new-registration-app/pyproject.toml b/new-registration-app/pyproject.toml index 9bbeba7..fd2893b 100644 --- a/new-registration-app/pyproject.toml +++ b/new-registration-app/pyproject.toml @@ -6,7 +6,7 @@ readme = "README.md" requires-python = "~=3.13.0" dependencies = [ "fastapi[standard]>=0.116.0", - "sqlmodel>=0.0.24", + "sqlalchemy>=2.0.44", "uvicorn[standard]>=0.35.0", ] @@ -16,4 +16,4 @@ dev = [ "isort>=6.0.1", ] [tool.isort] -profile = "black" \ No newline at end of file +profile = "black" diff --git a/new-registration-app/uv.lock b/new-registration-app/uv.lock index 831ce21..180fa37 100644 --- a/new-registration-app/uv.lock +++ b/new-registration-app/uv.lock @@ -321,7 +321,7 @@ version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "fastapi", extra = ["standard"] }, - { name = "sqlmodel" }, + { name = "sqlalchemy" }, { name = "uvicorn", extra = ["standard"] }, ] @@ -334,7 +334,7 @@ dev = [ [package.metadata] requires-dist = [ { 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" }, ] @@ -553,36 +553,23 @@ wheels = [ [[package]] name = "sqlalchemy" -version = "2.0.43" +version = "2.0.44" source = { registry = "https://pypi.org/simple" } 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 = "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 = [ - { 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/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/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/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/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/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/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/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/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" }, -] - -[[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" }, + { 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/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/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/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/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/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/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/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/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]]