Herança de templates em Python

Gostaria de entender como funciona a herança de templates.

Tenho algumas páginas porém tenho que ficar replicando as importações, a tag e queria entender como posso fazer para evitar esses códigos duplicados.

Template Engine

No resumo, é uma forma de escrever no HTML com conteúdo do backend.

Django, Flask e FastAPI possuem integração com o Jinja, que é um framework especifico para isso e bem conhecido pela comunidade.

Inclui alguns exemplos simples aqui mas o ideal seria ler a documentação no fim da publicação.

As principais tags usadas para herdar ou incluir templates são:

  • extends - para extender um HTML (usado uma estrutura base, para não replicar código)
  • block - quando queremos criar um bloco dinamico de HTML
  • include - quando queremos incluir um HTML

Existe outras como, recomendo dar uma lida em: Template Designer Documentation — Jinja Documentation (3.1.x)

Exemplo prático

Vamos criar um base.html que será a base para paginas que vamos herdar. Nesse arquivo vou criar a estrutura base de um HTML e adicionar 3 blocos (usando o block) para herdar tanto conteúdo HTML quanto Javascript/CSS de forma dinâmica.

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Meu site</title>
    <link rel="stylesheet" href="meu_css_global.css">
    {% block conteudo_css %}{% endblock %}
</head>
<body>

    {% block conteudo_html %}{% endblock %}


    <script src="meu_js_global.js"></script>
    {% block conteudo_js %}{% endblock %}
    
</body>
</html>

Beleza, entao o bloco conteudo_html vai renderizar algum html que vamos herdar.
O conteudo_css vai renderizar css customizado que vamos herdar.
O conteudo_js também vai renderizar javascript customizado que vamos herdar.

Perceba que tem também o meu_css_global.css e meu_js_global.js. Isso seria um exemplo de termos tanto Javascript quanto CSS que será usado em todas as páginas por padrão.

Herdando (extends)

Agora vamos criar 2 paginas que herdam o base.html e modificam só o conteúdo interno.

cadastrar.html

{% extends 'base.html' %}

{% block conteudo_css %}
<link rel="stylesheet" href="cadastrar.css">
{% endblock %}

{% block conteudo_html %}
<form action="/cadastrar" action="POST">
    <label for="idEmail">E-mail</label>
    <input type="email" name="email" id="idEmail">
    
    <button type="submit">Inscrever-se</button>
</form>
{% endblock %}

{% block conteudo_js %}
<script src="cadastrar.js"></script>
{% endblock %}

Perceba que na primeira linha usamos o extends para herdar a base do HTML.
Depois criamos os blocos de HTML (conteudo interno), CSS/Javascript (especificos da página de cadastro).

Resultado final

Após o Python e o Jinja compilarem os dados em HTML, o resultado final ao renderizar o arquivo cadastrar.html seria:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Meu site</title>
    <link rel="stylesheet" href="meu_css_global.css">
    <link rel="stylesheet" href="cadastrar.css">
</head>
<body>

    <form action="/cadastrar" action="POST">
        <label for="idEmail">E-mail</label>
        <input type="email" name="email" id="idEmail">
    
        <button type="submit">Inscrever-se</button>
    </form>


    <script src="meu_js_global.js"></script> 
    <script src="cadastrar.js"></script>
    
</body>
</html>

Pronto, bem tranquilo né? Recomendo praticar para explorar e calejar os primeiros erros de importação de template e coisa do tipo, é normal :sweat_smile:

Incluindo templates (include)

Além do block e extends temos também o include. A função dele é incluir um HTML.
Por exemplo, podemos ter um menu.html que se repete em várias páginas.

menu.html

<ul class="menu">
    <li>
        <a href="/">Inicio</a>
    </li>
    <li>
        <a href="/cadastrar">Cadastrar</a>
    </li>
    <li>
        <a href="/sobre">Sobre</a>
    </li>
</ul>

Considerando o exemplo dessa postagem, supondo que temos uma página de “Cadastrar” e uma página “Sobre”, poderíamos incluir o menu.html nas duas paginas, pra evitar duplicar código.

cadastrar.html

<!-- codigo anterior ... -->

{% include 'menu.html' %}

<!-- restante do código ... -->

sobre.html

<!-- codigo anterior ... -->

{% include 'menu.html' %}

<!-- restante do código ... -->

Atenção

Isso foi só um exemplo de inclusão de um HTML em diferentes páginas.
Nesse exemplo, o ideal seria incluir o menu no base.html, já que ele se repete em “todas as páginas” do exemplo.

Documentação

Para descobrir todas as funcionalidades interessantes que o Jinja oferece, veja a documentação:
https://jinja.palletsprojects.com/en/3.1.x/