Config -> common.py, added paths, respond.py functions and templates
This commit is contained in:
parent
bfea5eeb06
commit
804e07b555
6 changed files with 182 additions and 8 deletions
17
app/common.py
Normal file
17
app/common.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import secrets
|
||||
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from pydantic import BaseSettings
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
secret_key: str = secrets.token_hex(32)
|
||||
templates_dir: str = '../templates'
|
||||
static_dir: str = '../static'
|
||||
|
||||
settings = Settings()
|
||||
|
||||
|
||||
templates = Jinja2Templates(
|
||||
directory=settings.templates_dir,
|
||||
)
|
50
app/errors.py
Normal file
50
app/errors.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
"""Custom error pages for FastAPI app"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import Request, Response
|
||||
from fastapi import HTTPException
|
||||
|
||||
from . import paths
|
||||
from . import respond
|
||||
from .common import settings
|
||||
|
||||
# Add other HTTP error codes
|
||||
codes = [404, 500]
|
||||
|
||||
|
||||
class ErrorsPaths(paths.Paths):
|
||||
"""Sets up custom error pages,
|
||||
inherited from paths.Paths"""
|
||||
|
||||
def add_paths(self) -> None:
|
||||
|
||||
for code in codes:
|
||||
self.add_handler(code)
|
||||
|
||||
def add_handler(self, code: int) -> None:
|
||||
"""Adds an error handler to FastAPI app.
|
||||
Only for internal use!
|
||||
|
||||
Args:
|
||||
code (int): HTTP error code
|
||||
"""
|
||||
|
||||
tmpl_dir = (
|
||||
Path(__file__).parent /
|
||||
settings.templates_dir
|
||||
)
|
||||
file = tmpl_dir / f'{code}.html'
|
||||
|
||||
if not file.exists():
|
||||
return
|
||||
|
||||
@self.app.exception_handler(code)
|
||||
async def handler(req: Request, exc: HTTPException) -> Response:
|
||||
|
||||
return respond.with_tmpl(
|
||||
f'{code}.html',
|
||||
code=code,
|
||||
request=req,
|
||||
exc=exc,
|
||||
)
|
|
@ -1,16 +1,9 @@
|
|||
import secrets
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from pydantic import BaseSettings
|
||||
#from .common import settings, templates
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
secret_key: str = secrets.token_hex(32)
|
||||
|
||||
|
||||
settings = Settings()
|
||||
app = FastAPI()
|
||||
app.mount(
|
||||
'/static',
|
||||
|
|
16
app/pages.py
Normal file
16
app/pages.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""Main FastAPI paths"""
|
||||
|
||||
from fastapi import Response
|
||||
|
||||
from . import paths
|
||||
from . import respond
|
||||
|
||||
|
||||
class MainPaths(paths.Paths):
|
||||
"""Main FastAPI app paths, inherits paths.Paths"""
|
||||
|
||||
def add_paths(self) -> None:
|
||||
|
||||
@self.app.get('/')
|
||||
def index() -> Response:
|
||||
return respond.with_tmpl('index.html')
|
21
app/paths.py
Normal file
21
app/paths.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
import abc
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
class Paths(abc.ABC):
|
||||
"""Abstract class for storing paths for FastAPI app"""
|
||||
|
||||
def __init__(self, app: FastAPI) -> None:
|
||||
"""Abstract class for storing paths
|
||||
for FastAPI app
|
||||
|
||||
Args:
|
||||
app (FastAPI): Application object
|
||||
"""
|
||||
|
||||
self.app = app
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_paths(self) -> None:
|
||||
"""Add paths to the FastAPI application"""
|
77
app/respond.py
Normal file
77
app/respond.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
import os
|
||||
import mimetypes
|
||||
from typing import Optional, Mapping
|
||||
|
||||
from fastapi import Response
|
||||
from fastapi.responses import FileResponse
|
||||
from starlette.background import BackgroundTask
|
||||
|
||||
from .common import templates
|
||||
|
||||
|
||||
def with_tmpl(
|
||||
name: str,
|
||||
code: int = 200,
|
||||
headers: Optional[Mapping[str, str]] = None,
|
||||
background: Optional[BackgroundTask] = None,
|
||||
**context) -> Response:
|
||||
"""Render a Jinja2 template and return Response object.
|
||||
`response_class` parameter is not needed
|
||||
|
||||
Args:
|
||||
name (str): Template filename
|
||||
code (int, optional): HTTP response code
|
||||
headers (Optional[Mapping[str, str]], optional):
|
||||
Additional headers, passed to Response constructor
|
||||
background (Optional[BackgroundTask], optional):
|
||||
Background task, passed to Response constructor
|
||||
|
||||
Returns:
|
||||
FastAPI's TemplateResponse object
|
||||
"""
|
||||
|
||||
return templates.TemplateResponse(
|
||||
name=name,
|
||||
context=context,
|
||||
status_code=code,
|
||||
headers=headers,
|
||||
background=background,
|
||||
)
|
||||
|
||||
|
||||
def with_file(
|
||||
path: os.PathLike,
|
||||
mime: Optional[str] = None,
|
||||
code: int = 200,
|
||||
headers: Optional[Mapping[str, str]] = None,
|
||||
background: Optional[BackgroundTask] = None) -> FileResponse:
|
||||
"""Send a file specified in `path`
|
||||
automatically guessing mimetype if `mime` is None
|
||||
|
||||
Args:
|
||||
path (os.PathLike): File path
|
||||
mime (Optional[str], optional): File mimetype
|
||||
code (int, optional): HTTP response code
|
||||
headers (Optional[Mapping[str, str]], optional):
|
||||
Additional headers, passed to Response constructor
|
||||
background (Optional[BackgroundTask], optional):
|
||||
Background task, passed to Response constructor
|
||||
|
||||
Returns:
|
||||
FileResponse: FastAPI's FileResponse object
|
||||
"""
|
||||
|
||||
return FileResponse(
|
||||
path=path,
|
||||
media_type=(
|
||||
mime or
|
||||
mimetypes.guess_type(path)[0]
|
||||
),
|
||||
status_code=code,
|
||||
headers=headers,
|
||||
background=background,
|
||||
)
|
||||
|
||||
|
||||
# Alias
|
||||
with_template = with_tmpl
|
Loading…
Reference in a new issue