Return an html response
Level: Intermediate (score: 3)
You use FastAPI primarily to build APIs, but it can also be used as a web framework (although we think Django is a better option for this).
You can install Jinja2 and return a template response as shown here.
However on our platform we work with single test and exercise files so in this Bite you will return an HTMLResponse
with embedded HTML (docs). The effect will be the same though: the food log of a user in a nice html table!
Food | Added | Servings | Calories (kcal) |
egg | 2022-03-13 11:02:37.500968 | 1.5 x piece | 117.0 |
oatmeal | 2022-03-13 11:02:37.500968 | 2.0 x 100 grams | 672.0 |
We provided the show_foods_for_user
endpoint stub, add the logic to build up the HTML for the response. For your convenience we already added some template constants you can use: HTML_TEMPLATE
and TABLE_ROW
.
Here is what the final HTML response would look like as per the tests:
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Food log for Julian</title>
</head>
<body>
<table>
<thead>
<th>Food</th>
<th>Added</th>
<th>Servings</th>
<th>Calories (kcal)</th>
</thead>
<tbody>
<tr>
<td>egg</td>
<td>2022-03-13 11:02:37.500968</td>
<td>1.5 x piece</td>
<td>117.0</td>
</tr>
<tr>
<td>oatmeal</td>
<td>2022-03-13 11:02:37.500968</td>
<td>2.0 x 100 grams</td>
<td>672.0</td>
</tr>
</tbody>
</table>
</body>
</html>
Good luck and in the next and final FastAPI Bite we will add authentication to the API.
from datetime import datetime
from typing import Any, Dict, List
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from passlib.context import CryptContext
from pydantic import BaseModel
# https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/
# We'll export authentication further in a later Bite
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def get_password_hash(password):
return pwd_context.hash(password)
class Food(BaseModel):
id: int
name: str
serving_size: str
kcal_per_serving: int
protein_grams: float
fibre_grams: float = 0
class User(BaseModel):
id: int
username: str
password: str
def __init__(self, **data: Any):
data["password"] = get_password_hash(data["password"])
super().__init__(**data)
class FoodEntry(BaseModel):
id: int
user: User
food: Food
date_added: datetime = datetime.now()
number_servings: float
def total_calories(self):
return self.food.kcal_per_serving * self.number_servings
app = FastAPI()
food_log: Dict[int, FoodEntry] = {}
HTML_TEMPLATE = """<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Food log for {username}</title>
</head>
<body>
<table>
<thead>
<th>Food</th>
<th>Added</th>
<th>Servings</th>
<th>Calories (kcal)</th>
</thead>
<tbody>
{table_rows}
</tbody>
</table>
</body>
</html>"""
TABLE_ROW = """<tr>
<td>{food_name}</td>
<td>{date_added}</td>
<td>{number_servings} x {serving_size}</td>
<td>{total_calories}</td>
</tr>"""
post("/", status_code=201) .
async def create_food_entry(entry: FoodEntry):
"""Previous Bite and used in test"""
food_log[entry.id] = entry
return entry
get("/{username}", response_class=HTMLResponse) .
async def show_foods_for_user(request: Request, username: str):
# 1. extract foods for user using the food_log dict
# 2. build up the embedded html string
# 3. return an HTMLResponse (with the html content and status code 200)
pass