Drop Sqlmodel and use plain Sqlalchemy
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
"""
|
||||
|
||||
@@ -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"
|
||||
profile = "black"
|
||||
|
||||
39
new-registration-app/uv.lock
generated
39
new-registration-app/uv.lock
generated
@@ -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]]
|
||||
|
||||
Reference in New Issue
Block a user