-
-
Notifications
You must be signed in to change notification settings - Fork 401
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simple FastAPI example & helper functions (#318)
- Loading branch information
Showing
16 changed files
with
329 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
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,20 @@ | ||
.. _contrib_fastapi: | ||
|
||
================================ | ||
Tortoise-ORM FastAPI integration | ||
================================ | ||
|
||
We have a lightweight integration util ``tortoise.contrib.fastapi`` which has a single function ``register_tortoise`` which sets up Tortoise-ORM on startup and cleans up on teardown. | ||
|
||
FastAPI is basically Starlette & Pydantic, but in a very specific way. | ||
|
||
|
||
See the :ref:`example_fastapi` & have a look at the :ref:`contrib_pydantic` tutorials. | ||
|
||
Reference | ||
========= | ||
|
||
.. automodule:: tortoise.contrib.fastapi | ||
:members: | ||
:undoc-members: | ||
:show-inheritance: |
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 |
---|---|---|
|
@@ -9,6 +9,7 @@ Examples | |
|
||
examples/basic | ||
examples/pydantic | ||
examples/fastapi | ||
examples/quart | ||
examples/sanic | ||
examples/starlette |
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,27 @@ | ||
.. _example_fastapi: | ||
|
||
================ | ||
FastAPI Examples | ||
================ | ||
|
||
This is an example of the :ref:`contrib_fastapi` | ||
|
||
**Usage:** | ||
|
||
.. code-block:: sh | ||
uvicorn main:app --reload | ||
.. rst-class:: emphasize-children | ||
|
||
Basic non-relational example | ||
============================ | ||
|
||
models.py | ||
--------- | ||
.. literalinclude:: ../../examples/fastapi/models.py | ||
|
||
main.py | ||
------- | ||
.. literalinclude:: ../../examples/fastapi/main.py |
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 |
---|---|---|
|
@@ -10,3 +10,4 @@ quart | |
sanic | ||
starlette | ||
pydantic | ||
fastapi |
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,11 @@ | ||
Tortoise-ORM FastAPI example | ||
============================ | ||
|
||
We have a lightweight integration util ``tortoise.contrib.fastapi`` which has a single function ``register_tortoise`` which sets up Tortoise-ORM on startup and cleans up on teardown. | ||
|
||
Usage | ||
----- | ||
|
||
.. code-block:: sh | ||
uvicorn main:app --reload |
Empty file.
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 @@ | ||
# pylint: disable=E0611 | ||
from typing import List | ||
|
||
from fastapi import FastAPI, HTTPException | ||
from pydantic import BaseModel | ||
|
||
from models import User_Pydantic, UserIn_Pydantic, Users | ||
from tortoise.contrib.fastapi import HTTPNotFoundError, register_tortoise | ||
|
||
app = FastAPI(title="Tortoise ORM FastAPI example") | ||
|
||
|
||
class Status(BaseModel): | ||
message: str | ||
|
||
|
||
@app.get("/users", response_model=List[User_Pydantic]) | ||
async def get_users(): | ||
return await User_Pydantic.from_queryset(Users.all()) | ||
|
||
|
||
@app.post("/users", response_model=User_Pydantic) | ||
async def create_user(user: UserIn_Pydantic): | ||
user_obj = await Users.create(**user.dict(exclude_unset=True)) | ||
return await User_Pydantic.from_tortoise_orm(user_obj) | ||
|
||
|
||
@app.get( | ||
"/user/{user_id}", response_model=User_Pydantic, responses={404: {"model": HTTPNotFoundError}} | ||
) | ||
async def get_user(user_id: int): | ||
return await User_Pydantic.from_queryset_single(Users.get(id=user_id)) | ||
|
||
|
||
@app.post( | ||
"/user/{user_id}", response_model=User_Pydantic, responses={404: {"model": HTTPNotFoundError}} | ||
) | ||
async def update_user(user_id: int, user: UserIn_Pydantic): | ||
await Users.filter(id=user_id).update(**user.dict(exclude_unset=True)) | ||
return await User_Pydantic.from_queryset_single(Users.get(id=user_id)) | ||
|
||
|
||
@app.delete("/user/{user_id}", response_model=Status, responses={404: {"model": HTTPNotFoundError}}) | ||
async def delete_user(user_id: int): | ||
deleted_count = await Users.filter(id=user_id).delete() | ||
if not deleted_count: | ||
raise HTTPException(status_code=404, detail=f"User {user_id} not found") | ||
return Status(message=f"Deleted user {user_id}") | ||
|
||
|
||
register_tortoise( | ||
app, | ||
db_url="sqlite://:memory:", | ||
modules={"models": ["models"]}, | ||
generate_schemas=True, | ||
add_exception_handlers=True, | ||
) |
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,34 @@ | ||
from tortoise import fields, models | ||
from tortoise.contrib.pydantic import pydantic_model_creator | ||
|
||
|
||
class Users(models.Model): | ||
""" | ||
The User model | ||
""" | ||
|
||
id = fields.IntField(pk=True) | ||
#: This is a username | ||
username = fields.CharField(max_length=20, unique=True) | ||
name = fields.CharField(max_length=50, null=True) | ||
family_name = fields.CharField(max_length=50, null=True) | ||
category = fields.CharField(max_length=30, default="misc") | ||
password_hash = fields.CharField(max_length=128, null=True) | ||
created_at = fields.DatetimeField(auto_now_add=True) | ||
modified_at = fields.DatetimeField(auto_now=True) | ||
|
||
def full_name(self) -> str: | ||
""" | ||
Returns the best name | ||
""" | ||
if self.name or self.family_name: | ||
return f"{self.name or ''} {self.family_name or ''}".strip() | ||
return self.username | ||
|
||
class PydanticMeta: | ||
computed = ["full_name"] | ||
exclude = ["password_hash"] | ||
|
||
|
||
User_Pydantic = pydantic_model_creator(Users, name="User") | ||
UserIn_Pydantic = pydantic_model_creator(Users, name="UserIn", exclude_readonly=True) |
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 |
---|---|---|
|
@@ -54,3 +54,6 @@ uvicorn | |
|
||
# Pydantic support | ||
pydantic | ||
|
||
# FastAPI support | ||
fastapi |
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,114 @@ | ||
import logging | ||
from typing import Dict, List, Optional | ||
|
||
from fastapi import FastAPI, Request | ||
from fastapi.responses import JSONResponse | ||
from pydantic import BaseModel # pylint: disable=E0611 | ||
|
||
from tortoise import Tortoise | ||
from tortoise.exceptions import DoesNotExist, IntegrityError | ||
|
||
|
||
class HTTPNotFoundError(BaseModel): | ||
detail: str | ||
|
||
|
||
def register_tortoise( | ||
app: FastAPI, | ||
config: Optional[dict] = None, | ||
config_file: Optional[str] = None, | ||
db_url: Optional[str] = None, | ||
modules: Optional[Dict[str, List[str]]] = None, | ||
generate_schemas: bool = False, | ||
add_exception_handlers: bool = False, | ||
) -> None: | ||
""" | ||
Registers ``startup`` and ``shutdown`` events to set-up and tear-down Tortoise-ORM | ||
inside a FastAPI application. | ||
You can configure using only one of ``config``, ``config_file`` | ||
and ``(db_url, modules)``. | ||
Parameters | ||
---------- | ||
app: | ||
FastAPI app. | ||
config: | ||
Dict containing config: | ||
Example | ||
------- | ||
.. code-block:: python3 | ||
{ | ||
'connections': { | ||
# Dict format for connection | ||
'default': { | ||
'engine': 'tortoise.backends.asyncpg', | ||
'credentials': { | ||
'host': 'localhost', | ||
'port': '5432', | ||
'user': 'tortoise', | ||
'password': 'qwerty123', | ||
'database': 'test', | ||
} | ||
}, | ||
# Using a DB_URL string | ||
'default': 'postgres://postgres:qwerty123@localhost:5432/events' | ||
}, | ||
'apps': { | ||
'models': { | ||
'models': ['__main__'], | ||
# If no default_connection specified, defaults to 'default' | ||
'default_connection': 'default', | ||
} | ||
} | ||
} | ||
config_file: | ||
Path to .json or .yml (if PyYAML installed) file containing config with | ||
same format as above. | ||
db_url: | ||
Use a DB_URL string. See :ref:`db_url` | ||
modules: | ||
Dictionary of ``key``: [``list_of_modules``] that defined "apps" and modules that | ||
should be discovered for models. | ||
generate_schemas: | ||
True to generate schema immediately. Only useful for dev environments | ||
or SQLite ``:memory:`` databases | ||
add_exception_handlers: | ||
True to add some automatic exception handlers for ``DoesNotExist`` & ``IntegrityError``. | ||
This is not recommended for production systems as it may leak data. | ||
Raises | ||
------ | ||
ConfigurationError | ||
For any configuration error | ||
""" | ||
|
||
@app.on_event("startup") | ||
async def init_orm() -> None: # pylint: disable=W0612 | ||
await Tortoise.init(config=config, config_file=config_file, db_url=db_url, modules=modules) | ||
logging.info("Tortoise-ORM started, %s, %s", Tortoise._connections, Tortoise.apps) | ||
if generate_schemas: | ||
logging.info("Tortoise-ORM generating schema") | ||
await Tortoise.generate_schemas() | ||
|
||
@app.on_event("shutdown") | ||
async def close_orm() -> None: # pylint: disable=W0612 | ||
await Tortoise.close_connections() | ||
logging.info("Tortoise-ORM shutdown") | ||
|
||
if add_exception_handlers: | ||
|
||
@app.exception_handler(DoesNotExist) | ||
async def doesnotexist_exception_handler(request: Request, exc: DoesNotExist): | ||
return JSONResponse(status_code=404, content={"detail": str(exc)}) | ||
|
||
@app.exception_handler(IntegrityError) | ||
async def integrityerror_exception_handler(request: Request, exc: IntegrityError): | ||
return JSONResponse( | ||
status_code=422, | ||
content={"detail": {"loc": [], "msg": str(exc), "type": "IntegrityError"}}, | ||
) |
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
Oops, something went wrong.