Metadata-Version: 2.1
Name: class-based-fastapi
Version: 1.0.3
Summary: Class based routing for FastAPI
Home-page: https://github.com/XDeepZeroX/class-based-fastapi
Author: XDeepZeroX
License: MIT
Keywords: FastAPI,Class,Instance,Routing
Platform: all
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Framework :: FastAPI
Classifier: Framework :: AsyncIO
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Session
Classifier: Typing :: Typed
Requires-Python: >=3.8, <4
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi (>=0.73.0)
Requires-Dist: pydantic (>=1.8.2)
Provides-Extra: all
Requires-Dist: fastapi (>=0.73.0) ; extra == 'all'
Requires-Dist: pydantic (>=1.8.2) ; extra == 'all'
Requires-Dist: requests (>=2) ; extra == 'all'
Requires-Dist: uvicorn (>=0.18.2) ; extra == 'all'
Requires-Dist: pep8-naming (==0.13.1) ; extra == 'all'
Requires-Dist: flake8 (==4.0.1) ; extra == 'all'
Requires-Dist: flake8-annotations (==2.9.1) ; extra == 'all'
Requires-Dist: flake8-annotations-coverage (==0.0.6) ; extra == 'all'
Requires-Dist: flake8-bugbear (==22.7.1) ; extra == 'all'
Requires-Dist: flake8-builtins (==1.5.3) ; extra == 'all'
Requires-Dist: flake8-class-attributes-order (==0.1.3) ; extra == 'all'
Requires-Dist: flake8-cognitive-complexity (==0.1.0) ; extra == 'all'
Requires-Dist: flake8-commas (==2.1.0) ; extra == 'all'
Requires-Dist: flake8-comprehensions (==3.10.0) ; extra == 'all'
Requires-Dist: flake8-docstrings (==1.6.0) ; extra == 'all'
Requires-Dist: flake8-encodings (==0.5.0.post1) ; extra == 'all'
Requires-Dist: flake8-expression-complexity (==0.0.11) ; extra == 'all'
Requires-Dist: flake8-fastapi (==0.7.0) ; extra == 'all'
Requires-Dist: flake8-helper (==0.2.1) ; extra == 'all'
Requires-Dist: flake8-import-order (==0.18.1) ; extra == 'all'
Requires-Dist: flake8-multiline-containers (==0.0.19) ; extra == 'all'
Requires-Dist: flake8-newspaper-style (==0.3.0) ; extra == 'all'
Requires-Dist: flake8-pie (==0.15.0) ; extra == 'all'
Requires-Dist: flake8-plugin-utils (==1.3.2) ; extra == 'all'
Requires-Dist: flake8-polyfill (==1.0.2) ; extra == 'all'
Requires-Dist: flake8-quotes (==3.3.1) ; extra == 'all'
Requires-Dist: flake8-return (==1.1.3) ; extra == 'all'
Requires-Dist: flake8-scream (==0.1.0) ; extra == 'all'
Requires-Dist: flake8-simplify (==0.19.3) ; extra == 'all'
Requires-Dist: flake8-unused-arguments (==0.0.11) ; extra == 'all'
Requires-Dist: flake8-useless-assert (==0.4.3) ; extra == 'all'
Requires-Dist: flake8-variables-names (==0.0.5) ; extra == 'all'
Requires-Dist: mkdocs-material (>=8.4.3) ; extra == 'all'
Requires-Dist: pygments (>=2.13.0) ; extra == 'all'
Requires-Dist: mkdocs-glightbox (>=0.2.1) ; extra == 'all'
Requires-Dist: markdown-include (>=0.7.0) ; extra == 'all'
Requires-Dist: pytest (>=6.2.5) ; extra == 'all'
Requires-Dist: requests (>=2.28.1) ; extra == 'all'
Requires-Dist: sqlmodel (>=0.0.8) ; extra == 'all'
Provides-Extra: dev
Requires-Dist: requests (>=2) ; extra == 'dev'
Requires-Dist: uvicorn (>=0.18.2) ; extra == 'dev'
Requires-Dist: pep8-naming (==0.13.1) ; extra == 'dev'
Requires-Dist: flake8 (==4.0.1) ; extra == 'dev'
Requires-Dist: flake8-annotations (==2.9.1) ; extra == 'dev'
Requires-Dist: flake8-annotations-coverage (==0.0.6) ; extra == 'dev'
Requires-Dist: flake8-bugbear (==22.7.1) ; extra == 'dev'
Requires-Dist: flake8-builtins (==1.5.3) ; extra == 'dev'
Requires-Dist: flake8-class-attributes-order (==0.1.3) ; extra == 'dev'
Requires-Dist: flake8-cognitive-complexity (==0.1.0) ; extra == 'dev'
Requires-Dist: flake8-commas (==2.1.0) ; extra == 'dev'
Requires-Dist: flake8-comprehensions (==3.10.0) ; extra == 'dev'
Requires-Dist: flake8-docstrings (==1.6.0) ; extra == 'dev'
Requires-Dist: flake8-encodings (==0.5.0.post1) ; extra == 'dev'
Requires-Dist: flake8-expression-complexity (==0.0.11) ; extra == 'dev'
Requires-Dist: flake8-fastapi (==0.7.0) ; extra == 'dev'
Requires-Dist: flake8-helper (==0.2.1) ; extra == 'dev'
Requires-Dist: flake8-import-order (==0.18.1) ; extra == 'dev'
Requires-Dist: flake8-multiline-containers (==0.0.19) ; extra == 'dev'
Requires-Dist: flake8-newspaper-style (==0.3.0) ; extra == 'dev'
Requires-Dist: flake8-pie (==0.15.0) ; extra == 'dev'
Requires-Dist: flake8-plugin-utils (==1.3.2) ; extra == 'dev'
Requires-Dist: flake8-polyfill (==1.0.2) ; extra == 'dev'
Requires-Dist: flake8-quotes (==3.3.1) ; extra == 'dev'
Requires-Dist: flake8-return (==1.1.3) ; extra == 'dev'
Requires-Dist: flake8-scream (==0.1.0) ; extra == 'dev'
Requires-Dist: flake8-simplify (==0.19.3) ; extra == 'dev'
Requires-Dist: flake8-unused-arguments (==0.0.11) ; extra == 'dev'
Requires-Dist: flake8-useless-assert (==0.4.3) ; extra == 'dev'
Requires-Dist: flake8-variables-names (==0.0.5) ; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material (>=8.4.3) ; extra == 'docs'
Requires-Dist: pygments (>=2.13.0) ; extra == 'docs'
Requires-Dist: mkdocs-glightbox (>=0.2.1) ; extra == 'docs'
Requires-Dist: markdown-include (>=0.7.0) ; extra == 'docs'
Provides-Extra: test
Requires-Dist: pytest (>=6.2.5) ; extra == 'test'
Requires-Dist: requests (>=2.28.1) ; extra == 'test'
Requires-Dist: sqlmodel (>=0.0.8) ; extra == 'test'

<p align="center">
    <em>Class based routing for FastAPI</em>
</p>
<p align="center">
<img src="https://img.shields.io/github/last-commit/XDeepZeroX/class-based-fastapi.svg">
<br />
<a href="https://pypi.org/project/class-based-fastapi" target="_blank">
    <img src="https://img.shields.io/pypi/v/class-based-fastapi?label=class-based-fastapi" alt="Package version">
</a>
    <img src="https://img.shields.io/badge/python-3.6%20--%203.10-blue">
    <img src="https://img.shields.io/github/license/XDeepZeroX/class-based-fastapi">
</p>

---

**Documentation**:
<a href="https://XDeepZeroX.github.io/class-based-fastapi" target="_blank">https://XDeepZeroX.github.io/class-based-fastapi</a>

**Source Code**:
<a href="https://github.com/XDeepZeroX/class-based-fastapi" target="_blank">https://github.com/XDeepZeroX/class-based-fastapi</a>

---

<a href="https://fastapi.tiangolo.com">FastAPI</a> is a modern, fast web framework for building APIs with Python 3.6+.

---

## Features

Write Fast API Controllers (Classes) that can inherit route information from it's parent.

- This also allows to create a path prefix from a template and add api version information in the template.
- You don't need to duplicate the code, you can inherit it.
- To generate OpenAPI documentation, you do not need to explicitly specify the type of the return value, use Generics !

> Do the same with API methods as before, only more convenient.

See the [docs](https://XDeepZeroX.github.io/class-based-fastapi) for more details and examples.

## Requirements

This package is intended for use with any recent version of FastAPI (depending on `pydantic>=1.8.2`), and Python 3.6+.

## Installation

```sh
pip install class-based-fastapi
```

## Example


```python
import uuid
from typing import List, Generic, TypeVar  # 0. Import

import sqlalchemy
import uvicorn
from class_based_fastapi import Routable, get, put, post, delete
from fastapi import FastAPI, Depends
from sqlalchemy import select
from sqlmodel import Session, create_engine

from database import run_async_upgrade
from models.models import Category, CategoryPUT, Book, BookPUT

app = FastAPI(debug=True)

engine = create_engine('postgresql://postgres:123456@localhost:5432/fastapi_example', echo=True)


@app.on_event("startup")
def on_startup():
    print("Start migration")
    run_async_upgrade()
    print("DB success upgrade !")


def get_session() -> Session:
    with Session(engine) as conn:
        yield conn


T = TypeVar('T')  # 1. Create generic type
TPut = TypeVar('TPut')  # 1. Create generic type


class BaseAPI(Routable, Generic[T, TPut]):  # 2. Create generic base API controller
    conn: Session = Depends(get_session)

    def __init__(self):
        self._type_db_model = self._get_type_generic(T)

    def _get_type_generic(self, tvar: TypeVar):
        return next(filter(lambda x: x['name'] == tvar.__name__, self.__class__.__generic_attribute__))['type']

    @get("")
    def get_list_categories(self) -> List[T]:  # 3. Specifying  generic types
        items = self.conn.execute(select(self._type_db_model)).scalars().all()
        return items

    @post("")
    def add_category(self, model: T) -> T:
        self.conn.add(model)
        self.conn.commit()
        return model

    @delete("{guid}")
    def delete_category(self, guid: str) -> bool:
        self.conn.execute(
            sqlalchemy.delete(self._type_db_model).filter(self._type_db_model.guid == uuid.UUID(guid))
        )
        self.conn.commit()
        return True

    @put("{guid}")
    def update_category(self, guid: str, model: TPut) -> T:  # 3. Specifying  generic types
        model_db = self.conn.execute(
            select(self._type_db_model).filter(self._type_db_model.guid == uuid.UUID(guid))
        ).scalar()
        # Update fields
        for name, val in model.dict(exclude_unset=True).items():
            setattr(model_db, name, val)
        self.conn.commit()
        self.conn.refresh(model_db)
        return model_db


# Categories
class CategoryAPI(BaseAPI[Category, CategoryPUT]):  # 4. Inheriting the base controller
    NAME_MODULE = Category.__name__


# Books
class BookAPI(BaseAPI[Book, BookPUT]):  # 4. Inheriting the base controller
    NAME_MODULE = Book.__name__


app.include_router(CategoryAPI.routes())  # 5. Include routes
app.include_router(BookAPI.routes())  # 5. Include routes

if __name__ == "__main__":
    uvicorn.run('main:app', host="localhost", port=8001, reload=True, debug=True)

```

![Class base API OpenAPI Docs](https://github.com/XDeepZeroX/class-based-fastapi/raw/main/docs/img/generics/Class_based_API.png)

[Next steps >>>](https://XDeepZeroX.github.io/class-based-fastapi)

## License

This project is licensed under the terms of
the [MIT](https://github.com/XDeepZeroX/class-based-fastapi/blob/main/LICENSE) license.
