Skip to content

Commit ddcd8f6

Browse files
committed
feat(examples): use minijinja in place of jinja2
1 parent 2adad9f commit ddcd8f6

File tree

6 files changed

+115
-41
lines changed

6 files changed

+115
-41
lines changed

examples/body.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,30 @@
99
# HISTORY:
1010
# *************************************************************
1111
from fastapi import FastAPI, Request, Depends
12-
from fastapi.responses import JSONResponse
13-
from fastapi.templating import Jinja2Templates
12+
from fastapi.responses import HTMLResponse, JSONResponse
1413
from fastapi_csrf_protect import CsrfProtect
1514
from fastapi_csrf_protect.exceptions import CsrfProtectError
15+
from os import path
16+
from minijinja import Environment
1617
from pydantic_settings import BaseSettings
18+
from typing import Annotated
19+
20+
21+
def loader(name):
22+
segments: list[str] = []
23+
for segment in name.split("/"):
24+
if "\\" in segment or segment in (".", ".."):
25+
return None
26+
segments.append(segment)
27+
try:
28+
with open(path.join("templates", *segments)) as file:
29+
return file.read()
30+
except (IOError, OSError):
31+
pass
32+
1733

1834
app = FastAPI()
19-
templates = Jinja2Templates(directory="templates")
35+
environment = Environment(loader=loader)
2036

2137

2238
class CsrfSettings(BaseSettings):
@@ -32,13 +48,16 @@ def get_csrf_config():
3248
return CsrfSettings()
3349

3450

35-
@app.get("/")
36-
async def form(request: Request, csrf_protect: CsrfProtect = Depends()):
51+
@app.get("/", response_class=HTMLResponse)
52+
async def form(
53+
request: Request, csrf_protect: Annotated[CsrfProtect, Depends(CsrfProtect)]
54+
) -> HTMLResponse:
3755
"""
3856
Returns form template.
3957
"""
4058
csrf_token, signed_token = csrf_protect.generate_csrf_tokens()
41-
response = templates.TemplateResponse("form.html", {"request": request, "csrf_token": csrf_token})
59+
content = environment.render_template("form.html", csrf_token=csrf_token, request=request)
60+
response = HTMLResponse(content=content)
4261
csrf_protect.set_csrf_cookie(signed_token, response)
4362
return response
4463

examples/form_data.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,31 @@
99
# HISTORY:
1010
# *************************************************************
1111
from fastapi import FastAPI, Form, Request, Depends
12-
from fastapi.responses import JSONResponse
13-
from fastapi.templating import Jinja2Templates
12+
from fastapi.responses import HTMLResponse, JSONResponse
1413
from fastapi_csrf_protect import CsrfProtect
1514
from fastapi_csrf_protect.exceptions import CsrfProtectError
15+
from minijinja import Environment
16+
from os import path
1617
from pydantic import EmailStr, StrictStr
1718
from pydantic_settings import BaseSettings
19+
from typing import Annotated
20+
21+
22+
def loader(name):
23+
segments: list[str] = []
24+
for segment in name.split("/"):
25+
if "\\" in segment or segment in (".", ".."):
26+
return None
27+
segments.append(segment)
28+
try:
29+
with open(path.join("templates", *segments)) as file:
30+
return file.read()
31+
except (IOError, OSError):
32+
pass
33+
1834

1935
app = FastAPI()
20-
templates = Jinja2Templates(directory="templates")
36+
environment = Environment(loader=loader, reload_before_render=True)
2137

2238

2339
class CsrfSettings(BaseSettings):
@@ -33,13 +49,16 @@ def get_csrf_config():
3349
return CsrfSettings()
3450

3551

36-
@app.get("/")
37-
async def form(request: Request, csrf_protect: CsrfProtect = Depends()):
52+
@app.get("/", response_class=HTMLResponse)
53+
async def form(
54+
request: Request, csrf_protect: Annotated[CsrfProtect, Depends(CsrfProtect)]
55+
) -> HTMLResponse:
3856
"""
3957
Returns form template.
4058
"""
4159
csrf_token, signed_token = csrf_protect.generate_csrf_tokens()
42-
response = templates.TemplateResponse("form.html", {"request": request, "csrf_token": csrf_token})
60+
content: str = environment.render_template("form.html", csrf_token=csrf_token, request=request)
61+
response: HTMLResponse = HTMLResponse(content=content)
4362
csrf_protect.set_csrf_cookie(signed_token, response)
4463
return response
4564

examples/header.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,30 @@
99
# HISTORY:
1010
# *************************************************************
1111
from fastapi import FastAPI, Request, Depends
12-
from fastapi.responses import JSONResponse
13-
from fastapi.templating import Jinja2Templates
12+
from fastapi.responses import HTMLResponse, JSONResponse
1413
from fastapi_csrf_protect import CsrfProtect
1514
from fastapi_csrf_protect.exceptions import CsrfProtectError
15+
from minijinja import Environment
16+
from os import path
1617
from pydantic_settings import BaseSettings
18+
from typing import Annotated
19+
20+
21+
def loader(name):
22+
segments: list[str] = []
23+
for segment in name.split("/"):
24+
if "\\" in segment or segment in (".", ".."):
25+
return None
26+
segments.append(segment)
27+
try:
28+
with open(path.join("templates", *segments)) as file:
29+
return file.read()
30+
except (IOError, OSError):
31+
pass
32+
1733

1834
app = FastAPI()
19-
templates = Jinja2Templates(directory="templates")
35+
environment = Environment(loader=loader, reload_before_render=True)
2036

2137

2238
class CsrfSettings(BaseSettings):
@@ -30,15 +46,16 @@ def get_csrf_config():
3046
return CsrfSettings()
3147

3248

33-
@app.get("/")
34-
async def form(request: Request, csrf_protect: CsrfProtect = Depends()):
49+
@app.get("/", response_class=HTMLResponse)
50+
async def form(
51+
request: Request, csrf_protect: Annotated[CsrfProtect, Depends(CsrfProtect)]
52+
) -> HTMLResponse:
3553
"""
3654
Returns form template.
3755
"""
3856
csrf_token, signed_token = csrf_protect.generate_csrf_tokens()
39-
response = templates.TemplateResponse(
40-
"header.html", {"request": request, "csrf_token": csrf_token}
41-
)
57+
content = environment.render_template("header.html", csrf_token=csrf_token, request=request)
58+
response = HTMLResponse(content=content)
4259
csrf_protect.set_csrf_cookie(signed_token, response)
4360
return response
4461

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ dev = [
1010
'ruff >=0.4.7',
1111
]
1212
examples = [
13-
'Jinja2 >=3.0.1',
1413
'python-multipart >=0.0.6',
1514
'pydantic[email] >=1.7.2,<3.0.0',
1615
'uvicorn >=0.15.0',
16+
'minijinja >=2.11.0',
17+
'fastapi >=0.115.12',
1718
]
1819
tests = [
1920
'fastapi >=0',

templates/form.html

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,28 @@
11
<!DOCTYPE html>
22
<html>
33
<head>
4-
<title>
5-
Registration Form
6-
</title>
7-
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700' rel='stylesheet'>
4+
<link
5+
href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700'
6+
rel='stylesheet'
7+
>
88
<link
99
crossorigin='anonymous'
1010
href='https://use.fontawesome.com/releases/v5.4.1/css/all.css'
1111
integrity='sha384-5sAR7xN1Nv6T6+dT2mhtzEpVJvfS3NScPQTrOxhwjIuvcA67KV2R5Jz6kr4abQsz'
1212
rel='stylesheet'
1313
>
14+
<link
15+
href='data:image/png;base64,iVBORw0KGgo='
16+
rel='icon'
17+
>
1418
<style>
15-
html,
16-
body {
19+
body, html {
1720
display: flex;
1821
justify-content: center;
1922
height: 100%;
2023
}
2124

22-
body,
23-
div,
24-
h1,
25-
form,
26-
input,
27-
p {
25+
body, div, form, input, p {
2826
padding: 0;
2927
margin: 0;
3028
outline: none;
@@ -40,15 +38,18 @@
4038
text-align: center;
4139
}
4240

43-
p {
44-
font-size: 12px;
45-
}
46-
4741
hr {
4842
color: #a9a9a9;
4943
opacity: 0.3;
5044
}
45+
46+
p {
47+
font-size: 12px;
48+
}
5149
</style>
50+
<title>
51+
Registration Form
52+
</title>
5253
</head>
5354
<body>
5455
<div class='main-block'>
@@ -73,8 +74,8 @@ <h1>
7374
<input type='password' name='password' id='password' placeholder='Password' required/>
7475
<hr>
7576
<input type='submit' value='Submit'/>
76-
<input type='hidden' name='csrf-token' value="{{ csrf_token }}"/>
77+
<input type='hidden' name='csrf-token' value='{{ csrf_token }}'/>
7778
</form>
7879
</div>
7980
</body>
80-
</html>
81+
</html>

uv.lock

Lines changed: 19 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)