Created
November 22, 2024 18:06
-
-
Save axelknock/2a52bc818a70600652fe34b551fd629e to your computer and use it in GitHub Desktop.
Basic example of middleware to alter dataclass response from FT to json
This file contains hidden or 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
| from fasthtml.common import * | |
| # Create middleware to handle requests ending in .json | |
| class JSONRedirectMiddleware(MiddlewareBase): | |
| def __init__(self, app): | |
| self.app = app | |
| # pass along the request with the application/json header | |
| # if the route excepts it (as defined below, the response will be JSON) | |
| async def __call__(self, scope, receive, send): | |
| path = scope["path"] | |
| if path.endswith(".json"): | |
| # Remove .json suffix and add json header | |
| scope["path"] = path.rsplit(".json", 1)[0] | |
| scope["headers"] = [ | |
| (b"accept", b"application/json"), | |
| *[h for h in scope["headers"] if h[0] != b"accept"], | |
| ] | |
| return await self.app(scope, receive, send) | |
| middleware = [ | |
| Middleware(JSONRedirectMiddleware), | |
| ] | |
| app, rt = fast_app( | |
| live=True, | |
| middleware=middleware, | |
| ) | |
| # Define model with FT method, which FastHTML will handle by default | |
| @dataclass | |
| class Todo: | |
| id: int | |
| task: str | |
| done: bool | |
| def __ft__(self): | |
| return Li(id=f"todo-{self.id}")( | |
| "✓" if self.done else "❌", | |
| " ", | |
| self.task, | |
| Span( | |
| " ", | |
| A("FT", href=f"/todo/{self.id}"), | |
| " ", | |
| A("JSON", href=f"/todo/{self.id}.json"), | |
| ), | |
| ) | |
| # Define which classes should also have JSON responses when requested | |
| def add_response_chooser_to_classes(*classes): | |
| def response_method(self, req: Optional[Request] = None): | |
| if req and req.headers.get("accept") == "application/json": | |
| return JSONResponse(asdict(self)) | |
| return self.__ft__() | |
| for cls in classes: | |
| cls.__response__ = response_method | |
| add_response_chooser_to_classes(Todo) | |
| # Create and seed database | |
| def make_db(): | |
| db = Database("todos.db") | |
| todos = db.create(Todo, name="todos") | |
| # sample data | |
| if not todos.count > 0: | |
| todos.insert(Todo(task="Make gist", done=True), ignore=True) | |
| todos.insert(Todo(task="Share gist", done=False), ignore=True) | |
| return db, todos | |
| db, todos = make_db() | |
| @rt("/todo/{id}") | |
| async def todo(id: int): | |
| return todos[id] | |
| @rt("/") | |
| def index(): | |
| return Titled( | |
| "Todos with Middleware", | |
| Ul( | |
| *[ | |
| Div(hx_get=f"/todo/{todo.id}", hx_trigger="load", hx_swap="outerHTML") | |
| for todo in todos() | |
| ] | |
| ), | |
| ) | |
| serve() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment