Flask Full-Stack

Templates, Static Files and Layouts

35 min

Template inheritance

{# templates/base.html #}
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{% block title %}My App{% endblock %}</title>
  <meta name="csrf-token" content="{{ csrf_token() }}">
  <link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}">
</head>
<body>
  {% include "partials/header.html" %}
  <main>
    {% with messages = get_flashed_messages(with_categories=true) %}
      {% for category, message in messages %}
        <div class="alert alert-{{ category }}">{{ message }}</div>
      {% endfor %}
    {% endwith %}
    {% block content %}{% endblock %}
  </main>
</body>
</html>
{# templates/projects/list.html #}
{% extends "base.html" %}
{% block title %}Projects{% endblock %}
{% block content %}
  <h1>Projects</h1>
  {% for p in projects %}
    <a href="{{ url_for('projects.detail', slug=p.slug) }}">{{ p.title }}</a>
  {% else %}
    <p>No projects yet.</p>
  {% endfor %}
{% endblock %}

url_for, always

url_for('projects.detail', slug=p.slug) builds URLs from endpoint names, so changing a route path never breaks links. A missing endpoint raises an error immediately, which is easy to catch in tests.

Escaping and safety

Jinja2 escapes variables automatically: a project title containing <script> is shown as text. Never use |safe on user-provided content. If you render user Markdown, use a renderer that escapes HTML first, then mark the result safe.

Macros: reusable components

{# templates/macros.html #}
{% macro card(title, body, href) %}
  <a class="card" href="{{ href }}"><h3>{{ title }}</h3><p>{{ body }}</p></a>
{% endmacro %}
{% from "macros.html" import card %}
{{ card(p.title, p.summary, url_for('projects.detail', slug=p.slug)) }}

Context processors

Make values available in every template:

@app.context_processor
def inject_globals():
    return {"site_name": "PandaDev", "year": datetime.utcnow().year}

Flash messages

from flask import flash, redirect, url_for
flash("Project saved.", "success")
return redirect(url_for("projects.list"))

Use the Post/Redirect/Get pattern: after a successful POST, redirect, so refreshing the page does not resubmit the form.

Exercise

Build a base layout with header, flash messages and footer; a projects list page and a detail page using a card macro; and a 404 template registered with @app.errorhandler(404).

Check yourself

  • Why build links with url_for?
  • When is |safe acceptable?
  • What problem does Post/Redirect/Get solve?

I tuoi progressi sono salvati solo in questo browser.

Invia per la revisione (10 punti)