From 30ad5e28c57aefe29fddb3c0ce5a06676de762eb Mon Sep 17 00:00:00 2001 From: Conrad Date: Mon, 1 Apr 2024 18:28:18 +0200 Subject: [PATCH] Added fastapi CRUD --- creyPY/fastapi/__init__.py | 3 ++ creyPY/fastapi/app.py | 23 +++++++++++++ creyPY/fastapi/crud.py | 56 +++++++++++++++++++++++++++++++ creyPY/fastapi/models/__init__.py | 1 + creyPY/fastapi/models/base.py | 0 requirements.txt | 2 ++ setup.py | 2 +- 7 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 creyPY/fastapi/app.py create mode 100644 creyPY/fastapi/crud.py create mode 100644 creyPY/fastapi/models/__init__.py create mode 100644 creyPY/fastapi/models/base.py diff --git a/creyPY/fastapi/__init__.py b/creyPY/fastapi/__init__.py index 05b1fbe..d9da85c 100644 --- a/creyPY/fastapi/__init__.py +++ b/creyPY/fastapi/__init__.py @@ -1 +1,4 @@ +from .app import * # noqa +from .crud import * # noqa +from .models import * # noqa from .pagination import * # noqa diff --git a/creyPY/fastapi/app.py b/creyPY/fastapi/app.py new file mode 100644 index 0000000..1a4fcaa --- /dev/null +++ b/creyPY/fastapi/app.py @@ -0,0 +1,23 @@ +import re + +from fastapi.routing import APIRoute + + +# Swagger operation ID config +def generate_unique_id(route: APIRoute) -> str: + op_id = re.sub(r"{.*?}", "", route.path_format) # remove path parameters + operation_id = re.sub(r"\W", "_", op_id.replace("//", "/"))[ + 1: + ] # replace non-alphanumeric characters with underscores + assert route.methods + # if the route doesn't end with an underscore we should add one + if operation_id[-1] != "_": + operation_id += "_" + # If get method and no {} in the path, it should be called list + if "GET" in route.methods and "{" not in route.path_format: + operation_id = operation_id + "list" + else: + operation_id = ( + operation_id + list(route.methods)[0].lower() + ) # add first (and only) method to operation_id + return operation_id diff --git a/creyPY/fastapi/crud.py b/creyPY/fastapi/crud.py new file mode 100644 index 0000000..34a9a01 --- /dev/null +++ b/creyPY/fastapi/crud.py @@ -0,0 +1,56 @@ +from typing import Type, TypeVar +from uuid import UUID + +from fastapi import HTTPException +from pydantic import BaseModel +from sqlalchemy.orm import Session + +T = TypeVar("T") # TODO: bound=Base + + +def get_object_or_404(db_class: Type[T], id: UUID | str, db: Session, expunge: bool = False) -> T: + obj = db.query(db_class).filter(db_class.id == id).one_or_none() + if obj is None: + raise HTTPException(status_code=404, detail="The object does not exist.") + if expunge: + db.expunge(obj) + return obj + + +def create_obj_from_data( + data: BaseModel, model: Type[T], db: Session, additonal_data={}, exclude={} +) -> T: + obj = model(**data.model_dump(exclude=exclude) | additonal_data) + db.add(obj) + db.commit() + db.refresh(obj) + return obj + + +def update_obj_from_data( + data: BaseModel, + model: Type[T], + id: UUID | str, + db: Session, + partial: bool = False, + ignore_fields=[], + additional_data={}, + exclude={}, +) -> T: + obj = get_object_or_404(model, id, db) + data_dict = data.model_dump(exclude_unset=not partial, exclude=exclude) + data_dict.update(additional_data) # merge additional_data into data_dict + for field in data_dict: + if field not in ignore_fields: + setattr(obj, field, data_dict[field]) + db.commit() + db.refresh(obj) + return obj + + +def delete_object(db_class: Type[T], id: UUID | str, db: Session) -> None: + obj = db.query(db_class).filter(db_class.id == id).one_or_none() + if obj is None: + raise HTTPException(status_code=404, detail="The object does not exist.") + db.delete(obj) + db.commit() diff --git a/creyPY/fastapi/models/__init__.py b/creyPY/fastapi/models/__init__.py new file mode 100644 index 0000000..4b40b38 --- /dev/null +++ b/creyPY/fastapi/models/__init__.py @@ -0,0 +1 @@ +from .base import * # noqa diff --git a/creyPY/fastapi/models/base.py b/creyPY/fastapi/models/base.py new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt index 567edb8..c526f1e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,5 @@ fastapi==0.110.0 # Pagination fastapi-pagination==0.12.21 # Pagination sniffio==1.3.1 # Pagination starlette==0.36.3 # Pagination + +sqlalchemy==2.0.29 # SQLAlchemy diff --git a/setup.py b/setup.py index d45131f..30933e0 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import find_packages, setup setup( name="creyPY", - version="0.0.5", + version="0.0.7", description="My collection of Python and FastAPI shortcuts etc.", author="Conrad Großer", author_email="conrad@noah.tech",