-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial work with database * Update README.md * Create router folder * Delete events route * Add GitLens to devcontainer * Add more info about ERD * Add response models * Improve loading ENV variable CORS_ORIGINS * Update ERD & schemas * Reformat files for Ruff * Update database Base model * Update imports in main.py * Remove crud.py * Add database relationships! * TicketStatusEnum from models * Add SQLite3 Editor vscode extension * Split schemas to files * Fix Ruff warning * Update Ticket model * Update and add some routers * Add delete routes * Add Tickets PATCHing * Fix deprecated method of datetime * Manually merge `main.py` * Remove version from docker compose config file * Add mariadb support * Update vscode launch config * Add Event update route * Add libmariadb-dev installation to Ruff GitHub actions
- Loading branch information
1 parent
5f2153e
commit 24bcd12
Showing
21 changed files
with
786 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -163,3 +163,5 @@ cython_debug/ | |
# Docker compose production files | ||
docker-compose.yml | ||
docker-compose.yaml | ||
db/sql_lite.db | ||
mysql/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Python: FastAPI", | ||
"type": "debugpy", | ||
"request": "launch", | ||
"module": "uvicorn", | ||
"args": [ | ||
"app.main:app", | ||
"--reload" | ||
], | ||
"jinja": true, | ||
"justMyCode": true, | ||
"env": { | ||
"SQLALCHEMY_DATABASE_URL": "sqlite:///./db/sql_lite.db" | ||
} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
from sqlalchemy import create_engine # , MetaData | ||
from sqlalchemy.orm import sessionmaker, DeclarativeBase, Session # , Mapped | ||
from typing import Any | ||
import os | ||
|
||
SQLALCHEMY_DATABASE_URL = os.getenv("SQLALCHEMY_DATABASE_URL") | ||
if SQLALCHEMY_DATABASE_URL is None: | ||
raise ValueError("$SQLALCHEMY_DATABASE_URL is not defined") | ||
|
||
if "sqlite" in SQLALCHEMY_DATABASE_URL: | ||
engine = create_engine( | ||
SQLALCHEMY_DATABASE_URL, | ||
connect_args={ | ||
"check_same_thread": False | ||
}, # ...is needed only for SQLite. It's not needed for other databases. | ||
) | ||
else: | ||
engine = create_engine(SQLALCHEMY_DATABASE_URL) | ||
|
||
SessionLocal = sessionmaker( | ||
autocommit=False, autoflush=False, bind=engine, expire_on_commit=False | ||
) | ||
|
||
|
||
# class Base(DeclarativeBase): | ||
# # metadata = MetaData(schema="public") | ||
# pass | ||
|
||
|
||
class BaseModelMixin(DeclarativeBase): | ||
""" | ||
Base repository class that wraps CRUD methods plus some other useful stuff. | ||
""" | ||
|
||
__abstract__ = True # This tells SQLAlchemy not to create a table for this class | ||
# id: Mapped[int] | ||
|
||
@classmethod | ||
def get_by_id(cls, id: int, db_session: Session): | ||
""" | ||
@brief Gets an object by identifier | ||
@param id The identifier | ||
@param session The session | ||
@return The object by identifier or None if not found. | ||
""" | ||
obj = db_session.get(cls, id) | ||
return obj | ||
|
||
@classmethod | ||
def get_all(cls, db_session: Session) -> list: | ||
""" | ||
@brief Gets all objects | ||
@param session The session | ||
@return All objects | ||
""" | ||
try: | ||
return db_session.query(cls).order_by(cls.id).all() | ||
except Exception: | ||
return db_session.query(cls).all() | ||
|
||
@classmethod | ||
def get_limit(cls, db_session: Session, limit: int = 100) -> list: | ||
""" | ||
@brief Gets all objects | ||
@param session The session | ||
@return All objects | ||
""" | ||
try: | ||
return db_session.query(cls).limit(limit).order_by(cls.id).all() | ||
except Exception: | ||
return db_session.query(cls).limit(limit).all() | ||
|
||
@classmethod | ||
def get_count(cls, db_session: Session) -> int: | ||
""" | ||
@brief Gets the count of objects | ||
@param session The session | ||
@return The count of objects | ||
""" | ||
return db_session.query(cls).count() | ||
|
||
@classmethod | ||
def exists(cls, id: int, db_session: Session) -> bool: | ||
""" | ||
@brief Determines if the given object exists. | ||
@param id The identifier. | ||
@param session The session | ||
@return True if it exists, false if not | ||
""" | ||
return bool(db_session.query(cls).filter_by(id=id).count()) | ||
|
||
@classmethod | ||
def exists_cls(cls, db_session: Session) -> bool: | ||
""" | ||
@brief Determines if the given object exists | ||
@param session The session | ||
@return True if it exists, false if not | ||
""" | ||
return bool(db_session.query(cls).count()) | ||
|
||
@classmethod | ||
def create(cls, db_session: Session, **kwargs): | ||
""" | ||
@brief Creates an object | ||
@param session database session | ||
@param kwargs arguments | ||
@return The new object | ||
""" | ||
kwargs.pop("_sa_instance_state", None) | ||
obj = cls(**kwargs) | ||
db_session.add(obj) | ||
db_session.commit() | ||
db_session.refresh(obj) | ||
return obj | ||
|
||
@classmethod | ||
def update(cls, db_session: Session, id: int, **kwargs): | ||
""" | ||
@brief Updates the given object | ||
@param session database session | ||
@param id identifier | ||
@param kwargs arguments | ||
@return object if it succeeds, None if it fails | ||
""" | ||
obj = cls.get_by_id(id, db_session) | ||
if obj is None: | ||
return None | ||
|
||
for key, value in kwargs.items(): | ||
setattr(obj, key, value) | ||
db_session.commit() | ||
db_session.refresh(obj) | ||
return obj | ||
|
||
@classmethod | ||
def delete(cls, db_session: Session, id: int): | ||
""" | ||
@brief Deletes the given object | ||
@param session database session | ||
@param id identifier | ||
@return object if it succeeds, None if it fails | ||
""" | ||
obj = cls.get_by_id(id, db_session) | ||
if obj is None: | ||
return None | ||
db_session.delete(obj) | ||
db_session.commit() | ||
return obj | ||
|
||
@classmethod | ||
def get_by_param(cls, db_session: Session, param_name: str, param_value: Any): | ||
""" | ||
@brief Gets an object by identifier | ||
@warning This method might not work | ||
@param db session The session | ||
@param param_name Name of the parameter to search | ||
@param param_value Value of the parameter to search | ||
@return The object by identifier or None if not found | ||
""" | ||
column = getattr(cls, param_name, None) | ||
if column is None: | ||
return None | ||
obj = db_session.query(cls).filter(column == param_value).first() | ||
return obj |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from sqlalchemy import DateTime, Integer, String, ForeignKey, Enum | ||
from sqlalchemy.orm import Mapped, relationship, mapped_column | ||
import enum | ||
from app.database import BaseModelMixin | ||
|
||
|
||
class TicketStatusEnum(enum.Enum): | ||
new = 0 | ||
confirmed = 1 | ||
paid = 2 | ||
cancelled = 3 | ||
|
||
|
||
class Ticket(BaseModelMixin): | ||
__tablename__ = "tickets" | ||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) | ||
email: Mapped[str] = mapped_column(String(length=250)) | ||
firstname: Mapped[str] = mapped_column(String(length=250)) | ||
lastname: Mapped[str] = mapped_column(String(length=250)) | ||
order_date: Mapped[DateTime] = mapped_column(DateTime) | ||
status: Mapped[TicketStatusEnum] = mapped_column(Enum(TicketStatusEnum)) | ||
description: Mapped[str] = mapped_column(String(length=250), default="") | ||
# maybe there should be an attribute for ticket cancellation | ||
|
||
# Relationships | ||
group_id: Mapped[int] = mapped_column( | ||
ForeignKey("ticket_groups.id", ondelete="CASCADE") | ||
) | ||
group = relationship("TicketGroup", back_populates="tickets") | ||
|
||
|
||
class TicketGroup(BaseModelMixin): | ||
__tablename__ = "ticket_groups" | ||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True) | ||
name: Mapped[str] = mapped_column(String(length=250)) | ||
capacity: Mapped[int] = mapped_column(Integer) | ||
|
||
# Relationships | ||
event_id: Mapped[int] = mapped_column(ForeignKey("events.id", ondelete="CASCADE")) | ||
tickets = relationship("Ticket", back_populates="group", passive_deletes=True) | ||
event = relationship("Event", back_populates="ticket_groups") | ||
|
||
|
||
class Event(BaseModelMixin): | ||
__tablename__ = "events" | ||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True) | ||
name: Mapped[str] = mapped_column(String(length=250)) | ||
tickets_sales_start: Mapped[DateTime] = mapped_column(DateTime) | ||
tickets_sales_end: Mapped[DateTime] = mapped_column(DateTime) | ||
|
||
# Relationships | ||
ticket_groups = relationship( | ||
"TicketGroup", back_populates="event", passive_deletes=True | ||
) |
Empty file.
Oops, something went wrong.