aiohttp templates
In our examples so far, we’ve only been returning plain text. You can return
more complex HTML if you have templates. For this, templates can be used to
render HTML templates.
aiohttp is a core library without embedded templating tool, third party libraries need
to be installed to provide such functionality.
Let’s use officially supported aiohttp_jinja2
for famous jinja2
template engine
(http://aiohttp_jinja2.readthedocs.org/).
aiohttp-jinja2
Install the dependencies:
(.venv) python3.7 -m pip install -U jinja2 aiohttp-jinja2
Code structure:
/jinja_server.py
/templates/base.html
Sample HTML template:
<html>
<head>
<title>aiohttp page</title>
</head>
<body>
<div>
<h1><a href="/">My aiohttp server</a></h1>
</div>
<div>
<p>Date now: {{ current_date }}</p>
<p>Hello {{ username}}. </p>
</div>
</body>
</html>
Template rendering
In jinja_server.py, set up aiohttp-jinja2 and template directory:
import jinja2
import aiohttp_jinja2
app = web.Application()
aiohttp_jinja2.setup(
app, loader=jinja2.FileSystemLoader(os.path.join(os.getcwd(), "templates"))
)
To render a page using the template:
@routes.get('/{username}')
async def greet_user(request: web.Request) -> web.Response:
context = {
'username': request.match_info.get("username", ""),
'current_date': 'January 27, 2017'
}
response = aiohttp_jinja2.render_template("example.html", request,
context=context)
return response
Another alternative is applying @aiohttp_jinja2.template()
decorator to
web-handler:
@routes.get('/{username}')
@aiohttp_jinja2.template("example.html")
async def greet_user(request: web.Request) -> Dict[str, Any]:
context = {
'username': request.match_info.get("username", ""),
'current_date': 'January 27, 2017'
}
return content
Note, the great_user
signature has changed: it returns a jinja2 context
now. @aiohttp_jinja2.template()
decorator renders the context and returns
web.Response
object automatically.
Render posts list
@router.get("/")
@aiohttp_jinja2.template("index.html")
async def index(request: web.Request) -> Dict[str, Any]:
ret = []
db = request.config_dict["DB"]
async with db.execute("SELECT id, owner, editor, title FROM posts") as cursor:
async for row in cursor:
ret.append(
{
"id": row["id"],
"owner": row["owner"],
"editor": row["editor"],
"title": row["title"],
}
)
return {"posts": ret}
index.html
template:
{% extends "base.html" %}
{% block title %}
Posts index
{% endblock %}
{% block content %}
<h1>Posts</h1>
<p>
<ul>
{% for post in posts %}
<li>
<a href="/{{ post.id }}">{{ post.title }}</a> {{ post.editor }}
</li>
{% endfor %}
</ul>
</p>
<hr>
<p>
<a href="/new">New</a>
</p>
{% endblock %}
base.html
for template inheritance:
<!DOCTYPE html>
<html lang="en">
<head>
<title>
{% block title %}
Index
{% endblock %}
</title>
</head>
<body>
<div id="content"> {% block content %} {% endblock %} </div>
</body>
</html>
Post editing
Multipart content
We use method="POST" enctype="multipart/form-data"
to send form data.
Sent body looks like:
------WebKitFormBoundaryw6YN2HqrOi6hewhP
Content-Disposition: form-data; name="title"
title 1
------WebKitFormBoundaryw6YN2HqrOi6hewhP
Content-Disposition: form-data; name="text"
text of post
------WebKitFormBoundaryw6YN2HqrOi6hewhP--
HTML site endpoints
- GET /
List posts.
- GET /new
Show form for adding post
- POST /new
Apply post adding
- GET /{post}/view
Show post
- GET /{post}/edit
Edit post
- GET /{post}/edit
Show edit post form
- POST /{post}/edit
Apply post editing
- GET /{post}/delete
Delete post
Note
URLs order does matter: /new
should lead /{post}
, otherwise /{post}
web
handler is called with new
post id.