Modelagem com Jinja2 no Flask: Essentials

Jinja2 é um mecanismo de modelo escrito em Python puro. Ele fornece uma sintaxe não XML inspirada no Django, mas suporta expressões inline e um ambiente de sandbox opcional. É pequeno, mas rápido, além de ser um mecanismo de modelo autônomo fácil de usar. Flask é um micro framework web baseado em Python que permite que você escreva seus aplicativos web de forma rápida e eficiente.

Nesta série de três partes, começarei com os fundamentos da modelagem Jinja2 da perspectiva do Flask. Nas partes subseqüentes desta série, abordarei tópicos avançados de modelagem enquanto aprendo como dispor modelos em um aplicativo baseado em Flask em um design modular e extensível.

Presumo que você tenha um entendimento básico das melhores práticas de configuração do Flask e do ambiente usando virtualenv a serem seguidas ao desenvolver um aplicativo Python.

Instalando Pacotes

O Flask vem empacotado com o Jinja2 e, portanto, só precisamos instalar o Flask. Para esta série, recomendo usar a versão de desenvolvimento do Flask, que inclui suporte de linha de comando muito mais estável, entre muitos outros recursos e melhorias no Flask em geral.

1
pip install https://github.com/mitsuhiko/flask/tarball/master

Precisa de um mecanismo de modelagem?

No Flask, podemos escrever um aplicativo da Web completo sem a necessidade de nenhum mecanismo de modelagem de terceiros. Vamos dar uma olhada em um pequeno Hello World aplicativo abaixo:

1
from flask import Flask
2
3
app = Flask(__name__)
4
5
@app.route('/')
6
@app.route('/hello')
7
@app.route('/hello/')
8
def hello_world(user=None):
9
    user = user or 'Shalabh'
10
    return '''

11
    

12
        

13
            Templating in Flask

14
        

15
        

16
            

Hello %s!

17
            

Welcome to the world of Flask!

18
        

19
    ''' % user
20
       
21
if __name__ == '__main__':
22
    app.run()

É óbvio que o padrão acima de escrever um aplicativo não é viável no caso de um aplicativo da Web real em que os códigos HTML, CSS e JS variam em milhares de linhas de código. Aqui, a modelagem nos salva porque podemos estruturar nosso código de exibição mantendo nossos modelos separados. Flask fornece suporte para Jinja2 por padrão, mas qualquer outro mecanismo de modelagem também pode ser usado conforme adequado.

Modelos de layout

Por padrão, o Flask espera que os modelos sejam colocados em uma pasta chamada templates no nível raiz do aplicativo. O Flask então lê automaticamente o conteúdo disponibilizando esta pasta para uso com o render_template() método. Vou demonstrar o mesmo reestruturando o trivial Hello World aplicativo mostrado acima.

A estrutura do aplicativo seria como mostrado abaixo.

1
flask_app/
2
    my_app.py    
3
    templates/
4
        - index.html

O próprio aplicativo

flask_app/my_app.py

1
from flask import Flask, render_template, request
2
3
app = Flask(__name__)
4
5
@app.route('/')
6
@app.route('/hello')
7
@app.route('/hello/')
8
def hello_world(user=None):
9
    user = user or 'Shalabh'
10
    return render_template('index.html', user=user)

flask_app/templates/index.html

1

2
  
3
    </span>Templating in Flask<span style="color: #bb0066;font-weight: bold">
4
  
5
  
6
    

Hello {{ user }}!

7
    

Welcome to the world of Flask!

8
  
9

Para executar o aplicativo, basta executar o seguinte comando na linha de comando:

Abra http://127.0.0.1:5000/ em um navegador para ver o aplicativo em ação. O resultado seria o mesmo no caso de http://127.0.0.1:5000/hello também.

A página de destino padrão

Tente abrir o URL com seu nome como a última parte dele. Portanto, se seu nome for John, a URL seria http://127.0.0.1:5000/hello/John. Agora a página ficaria assim:

Acessando a página de destino com um nome personalizado

É bastante simples que no método hello_world a última parte do URL depois hello é obtido da solicitação e passado para o contexto do modelo sendo renderizado usando render_template(). Esse valor é analisado a partir do contexto do modelo usando o espaço reservado Jinja2 {{ user }}. Este placeholder avalia todas as expressões que são colocadas dentro dele, dependendo do contexto do template.

Entendendo Blocos e Herança em Templates

Normalmente, qualquer aplicativo da Web terá várias páginas da Web que serão diferentes umas das outras. Os blocos de código, como cabeçalhos e rodapés, serão os mesmos em quase todas as páginas do site. Da mesma forma, o menu também permanece o mesmo. Na verdade, geralmente, apenas o bloco do contêiner central muda e o resto geralmente permanece o mesmo. Para isso, Jinja2 fornece uma ótima forma de herança entre templates. É uma boa prática ter um modelo base onde possamos estruturar o layout básico do site junto com o cabeçalho e o rodapé.

Vou criar um pequeno aplicativo para mostrar uma lista de produtos em diferentes categorias. Para estilizar, usarei o framework Bootstrap para dar um design básico aos templates. A estrutura do aplicativo agora é como mostrado abaixo.

1
flask_app/
2
    my_app.py    
3
    templates/
4
        - base.html
5
        - home.html
6
        - product.html
7
    static/
8
        css/
9
            - main.css

O restante do código do aplicativo é demonstrado a seguir.

flask_app/my_app.py

1
from flask import Flask, render_template, abort
2
3
app = Flask(__name__)
4
5
PRODUCTS = {
6
    'iphone': {
7
        'name': 'iPhone 5S',
8
        'category': 'Phones',
9
        'price': 699,
10
    },
11
    'galaxy': {
12
        'name': 'Samsung Galaxy 5',
13
        'category': 'Phones',
14
        'price': 649,
15
    },
16
    'ipad-air': {
17
        'name': 'iPad Air',
18
        'category': 'Tablets',
19
        'price': 649,
20
    },
21
    'ipad-mini': {
22
        'name': 'iPad Mini',
23
        'category': 'Tablets',
24
        'price': 549
25
    }
26
}
27
28
@app.route('/')
29
@app.route('/home')
30
def home():
31
    return render_template('home.html', products=PRODUCTS)
32
33
@app.route('/product/')
34
def product(key):
35
    product = PRODUCTS.get(key)
36
    if not product:
37
        abort(404)
38
    return render_template('product.html', product=product)

Neste arquivo, codifiquei a lista de produtos para tornar o aplicativo mais simples e focar apenas na parte de modelagem. Eu criei dois endpoints, home e productonde o primeiro serve para listar todos os produtos e o segundo abre a página individual.

flask_app/static/css/main.css

1
body {
2
  padding-top: 50px;
3
}
4
.top-pad {
5
  padding: 40px 15px;
6
  text-align: center;
7
}

Este arquivo contém um pouco de CSS personalizado que adicionei para tornar os modelos mais legíveis. Vamos ver os modelos agora.

flask_app/templates/base.html

1

2
 lang="en">
3
  
4
     charset="utf-8">
5
     http-equiv="X-UA-Compatible" content="IE=edge">
6
     name="viewport" content="width=device-width, initial-scale=1">
7
    </span>Jinja2 Tutorial - Tutsplus<span style="color: #bb0066;font-weight: bold">
8
     rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
9
     href="{{ url_for('static', filename="css/main.css") }}" rel="stylesheet">
10
  
11
  
12
     class="navbar navbar-inverse navbar-fixed-top" role="navigation">
13
       class="container">
14
         class="navbar-header">
15
           class="navbar-brand" href="{{ url_for('home')}}">Tutsplus - Jinja2 Tutorial
16
        
17
      
18
    
19
     class="container">
20
      {% block container %}{% endblock %}
21
    
22
    
23
    
24
    
25
    
26
27
  
28

Observe o uso de url_for() para criar URLs para arquivos estáticos e outros links. É uma ferramenta muito útil fornecida pela Flask. Leia mais sobre isso na documentação. Outro ponto importante a ser observado aqui é o uso de {% block container %}{% endblock %}, que é um componente crucial do trabalho do Jinja2 para tornar os modelos modulares e herdáveis. Os próximos arquivos deixarão isso mais claro.

Controle de fluxo

Python usa if, elif, elsee for instruções de loop para controlar o fluxo do programa com base em uma condição verdadeira ou falsa. Você também pode incorporar essas condições em seu aplicativo Flask para percorrer o dicionário de produtos e exibi-los na página inicial. Essas declarações condicionais são colocadas dentro {%  %} blocos. Atualize o home.html página da seguinte forma.

flask_app/templates/home.html

1
{% extends 'base.html' %}
2
 
3
{% block container %}
4
   class="top-pad">
5
    {% for id, product in products.items() %}
6
       class="well">
7
        

8
           href="{{ url_for('product', key=id) }}">{{product['name']}}
9
          $ {{ product['price']}}
10
        
11
      
12
    {% endfor %}
13
  
14
{% endblock %}

Veja como este modelo se estende base.html e fornece o conteúdo de {% block container %}. {% for %} se comporta exatamente como um loop for normal em qualquer linguagem que estamos usando aqui para criar uma lista de produtos.

flask_app/templates/product.html

1
{% extends 'home.html' %}
2
3
{% block container %}
4
  <div class="top-pad">
5
    <h1>{{ product['name'] }}
6
      <small>{{ product['category'] }}small>
7
    h1>
8
    <h3>$ {{ product['price'] }}h3>
9
  div>
10
{% endblock %}

O modelo acima implementa a página de produto individual.

Agora execute o aplicativo executando o seguinte comando.

O aplicativo em execução seria semelhante ao mostrado na captura de tela abaixo. Basta abrir http://127.0.0.1:5000/home no navegador.

O aplicativo em execução com o framework Bootstrap

Clique em qualquer um dos produtos para ver a página do produto individual.

Uma página de produto individual

Filtros

Os filtros podem ser aplicados a variáveis ​​da mesma forma que você usaria métodos Python. Uma barra vertical separa os filtros na linguagem de modelagem Jinja. Por exemplo, suponha que você precise colocar o nome do produto em maiúscula na página inicial; você aplicaria o upper filtrar da seguinte forma.

1
{% extends 'base.html' %}
2
 
3
{% block container %}
4
   class="top-pad">
5
    {% for id, product in products.items() %}
6
       class="well">
7
        

8
           href="{{ url_for('product', key=id) }}">{{product['name']| upper }}
9
          $ {{ product['price']}}
10
        
11
      
12
    {% endfor %}
13
  
14
{% endblock %}

Agora, se você executar o servidor, deverá observar que o nome do produto está em ALL CAPS.

Conclusão

Neste tutorial, vimos como dispor a estrutura do modelo em um aplicativo baseado em Flask usando Jinja2. Também vimos como os blocos podem ser usados ​​para alavancar a herança em modelos.

Na próxima parte desta série, veremos como escrever um filtro personalizado, um processador de contexto personalizado e uma macro.

Este post foi atualizado com contribuições de Esther Vaati. Esther é desenvolvedora de software e escritora da Envato Tuts+.

Deixe uma resposta