rewrite tutorial docs and example
This commit is contained in:
parent
16d83d6bb4
commit
c3dd7b8e4c
103 changed files with 3327 additions and 2224 deletions
|
|
@ -1,113 +1,187 @@
|
|||
.. _tutorial-templates:
|
||||
.. currentmodule:: flask
|
||||
|
||||
Step 7: The Templates
|
||||
=====================
|
||||
Templates
|
||||
=========
|
||||
|
||||
Now it is time to start working on the templates. As you may have
|
||||
noticed, if you make requests with the app running, you will get
|
||||
an exception that Flask cannot find the templates. The templates
|
||||
are using `Jinja2`_ syntax and have autoescaping enabled by
|
||||
default. This means that unless you mark a value in the code with
|
||||
:class:`~flask.Markup` or with the ``|safe`` filter in the template,
|
||||
Jinja2 will ensure that special characters such as ``<`` or ``>`` are
|
||||
escaped with their XML equivalents.
|
||||
You've written the authentication views for your application, but if
|
||||
you're running the server and try to go to any of the URLs, you'll see a
|
||||
``TemplateNotFound`` error. That's because the views are calling
|
||||
:func:`render_template`, but you haven't written the templates yet.
|
||||
The template files will be stored in the ``templates`` directory inside
|
||||
the ``flaskr`` package.
|
||||
|
||||
We are also using template inheritance which makes it possible to reuse
|
||||
the layout of the website in all pages.
|
||||
Templates are files that contain static data as well as placeholders
|
||||
for dynamic data. A template is rendered with specific data to produce a
|
||||
final document. Flask uses the `Jinja`_ template library to render
|
||||
templates.
|
||||
|
||||
Create the follwing three HTML files and place them in the
|
||||
:file:`templates` folder:
|
||||
In your application, you will use templates to render `HTML`_ which
|
||||
will display in the user's browser. In Flask, Jinja is configured to
|
||||
*autoescape* any data that is rendered in HTML templates. This means
|
||||
that it's safe to render user input; any characters they've entered that
|
||||
could mess with the HTML, such as ``<`` and ``>`` will be *escaped* with
|
||||
*safe* values that look the same in the browser but don't cause unwanted
|
||||
effects.
|
||||
|
||||
.. _Jinja2: http://jinja.pocoo.org/docs/templates
|
||||
Jinja looks and behaves mostly like Python. Special delimiters are used
|
||||
to distinguish Jinja syntax from the static data in the template.
|
||||
Anything between ``{{`` and ``}}`` is an expression that will be output
|
||||
to the final document. ``{%`` and ``%}`` denotes a control flow
|
||||
statement like ``if`` and ``for``. Unlike Python, blocks are denoted
|
||||
by start and end tags rather than indentation since static text within
|
||||
a block could change indentation.
|
||||
|
||||
layout.html
|
||||
-----------
|
||||
.. _Jinja: http://jinja.pocoo.org/docs/templates/
|
||||
.. _HTML: https://developer.mozilla.org/docs/Web/HTML
|
||||
|
||||
This template contains the HTML skeleton, the header and a link to log in
|
||||
(or log out if the user was already logged in). It also displays the
|
||||
flashed messages if there are any. The ``{% block body %}`` block can be
|
||||
replaced by a block of the same name (``body``) in a child template.
|
||||
|
||||
The :class:`~flask.session` dict is available in the template as well and
|
||||
you can use that to check if the user is logged in or not. Note that in
|
||||
Jinja you can access missing attributes and items of objects / dicts which
|
||||
makes the following code work, even if there is no ``'logged_in'`` key in
|
||||
the session:
|
||||
The Base Layout
|
||||
---------------
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
Each page in the application will have the same basic layout around a
|
||||
different body. Instead of writing the entire HTML structure in each
|
||||
template, each template will *extend* a base template and override
|
||||
specific sections.
|
||||
|
||||
.. code-block:: html+jinja
|
||||
:caption: ``flaskr/templates/base.html``
|
||||
|
||||
<!doctype html>
|
||||
<title>Flaskr</title>
|
||||
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
|
||||
<div class=page>
|
||||
<title>{% block title %}{% endblock %} - Flaskr</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
<nav>
|
||||
<h1>Flaskr</h1>
|
||||
<div class=metanav>
|
||||
{% if not session.logged_in %}
|
||||
<a href="{{ url_for('login') }}">log in</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('logout') }}">log out</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% for message in get_flashed_messages() %}
|
||||
<div class=flash>{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
|
||||
show_entries.html
|
||||
-----------------
|
||||
|
||||
This template extends the :file:`layout.html` template from above to display the
|
||||
messages. Note that the ``for`` loop iterates over the messages we passed
|
||||
in with the :func:`~flask.render_template` function. Notice that the form is
|
||||
configured to submit to the `add_entry` view function and use ``POST`` as
|
||||
HTTP method:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
{% if session.logged_in %}
|
||||
<form action="{{ url_for('add_entry') }}" method=post class=add-entry>
|
||||
<dl>
|
||||
<dt>Title:
|
||||
<dd><input type=text size=30 name=title>
|
||||
<dt>Text:
|
||||
<dd><textarea name=text rows=5 cols=40></textarea>
|
||||
<dd><input type=submit value=Share>
|
||||
</dl>
|
||||
</form>
|
||||
{% endif %}
|
||||
<ul class=entries>
|
||||
{% for entry in entries %}
|
||||
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}</li>
|
||||
{% else %}
|
||||
<li><em>Unbelievable. No entries here so far</em></li>
|
||||
{% endfor %}
|
||||
<ul>
|
||||
{% if g.user %}
|
||||
<li><span>{{ g.user['username'] }}</span>
|
||||
<li><a href="{{ url_for('auth.logout') }}">Log Out</a>
|
||||
{% else %}
|
||||
<li><a href="{{ url_for('auth.register') }}">Register</a>
|
||||
<li><a href="{{ url_for('auth.login') }}">Log In</a>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
<section class="content">
|
||||
<header>
|
||||
{% block header %}{% endblock %}
|
||||
</header>
|
||||
{% for message in get_flashed_messages() %}
|
||||
<div class="flash">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% block content %}{% endblock %}
|
||||
</section>
|
||||
|
||||
:data:`g` is automatically available in templates. Based on if
|
||||
``g.user`` is set (from ``load_logged_in_user``), either the username
|
||||
and a log out link are displayed, otherwise links to register and log in
|
||||
are displayed. :func:`url_for` is also automatically available, and is
|
||||
used to generate URLs to views instead of writing them out manually.
|
||||
|
||||
After the page title, and before the content, the template loops over
|
||||
each message returned by :func:`get_flashed_messages`. You used
|
||||
:func:`flash` in the views to show error messages, and this is the code
|
||||
that will display them.
|
||||
|
||||
There are three blocks defined here that will be overridden in the other
|
||||
templates:
|
||||
|
||||
#. ``{% block title %}`` will change the title displayed in the
|
||||
browser's tab and window title.
|
||||
|
||||
#. ``{% block header %}`` is similar to ``title`` but will change the
|
||||
title displayed on the page.
|
||||
|
||||
#. ``{% block content %}`` is where the content of each page goes, such
|
||||
as the login form or a blog post.
|
||||
|
||||
The base template is directly in the ``templates`` directory. To keep
|
||||
the others organized, the templates for a blueprint will be placed in a
|
||||
directory with the same name as the blueprint.
|
||||
|
||||
|
||||
Register
|
||||
--------
|
||||
|
||||
.. code-block:: html+jinja
|
||||
:caption: ``flaskr/templates/auth/register.html``
|
||||
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block header %}
|
||||
<h1>{% block title %}Register{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
login.html
|
||||
----------
|
||||
|
||||
This is the login template, which basically just displays a form to allow
|
||||
the user to login:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<h2>Login</h2>
|
||||
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
|
||||
<form action="{{ url_for('login') }}" method=post>
|
||||
<dl>
|
||||
<dt>Username:
|
||||
<dd><input type=text name=username>
|
||||
<dt>Password:
|
||||
<dd><input type=password name=password>
|
||||
<dd><input type=submit value=Login>
|
||||
</dl>
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
<label for="username">Username</label>
|
||||
<input name="username" id="username" required>
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" id="password" required>
|
||||
<input type="submit" value="Register">
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
Continue with :ref:`tutorial-css`.
|
||||
``{% extends 'base.html' %}`` tells Jinja that this template should
|
||||
replace the blocks from the base template. All the rendered content must
|
||||
appear inside ``{% block %}`` tags that override blocks from the base
|
||||
template.
|
||||
|
||||
A useful pattern used here is to place ``{% block title %}`` inside
|
||||
``{% block header %}``. This will set the title block and then output
|
||||
the value of it into the header block, so that both the window and page
|
||||
share the same title without writing it twice.
|
||||
|
||||
The ``input`` tags are using the ``required`` attribute here. This tells
|
||||
the browser not to submit the form until those fields are filled in. If
|
||||
the user is using an older browser that doesn't support that attribute,
|
||||
or if they are using something besides a browser to make requests, you
|
||||
still want to validate the data in the Flask view. It's important to
|
||||
always fully validate the data on the server, even if the client does
|
||||
some validation as well.
|
||||
|
||||
|
||||
Log In
|
||||
------
|
||||
|
||||
This is identical to the register template except for the title and
|
||||
submit button.
|
||||
|
||||
.. code-block:: html+jinja
|
||||
:caption: ``flaskr/templates/auth/login.html``
|
||||
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block header %}
|
||||
<h1>{% block title %}Log In{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
<label for="username">Username</label>
|
||||
<input name="username" id="username" required>
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" id="password" required>
|
||||
<input type="submit" value="Log In">
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
Register A User
|
||||
---------------
|
||||
|
||||
Now that the authentication templates are written, you can register a
|
||||
user. Make sure the server is still running (``flask run`` if it's not),
|
||||
then go to http://127.0.0.1:5000/auth/register.
|
||||
|
||||
Try clicking the "Register" button without filling out the form and see
|
||||
that the browser shows an error message. Try removing the ``required``
|
||||
attributes from the ``register.html`` template and click "Register"
|
||||
again. Instead of the browser showing an error, the page will reload and
|
||||
the error from :func:`flash` in the view will be shown.
|
||||
|
||||
Fill out a username and password and you'll be redirected to the login
|
||||
page. Try entering an incorrect username, or the correct username and
|
||||
incorrect password. If you log in you'll get an error because there's
|
||||
no ``index`` view to redirect to yet.
|
||||
|
||||
Continue to :doc:`static`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue