Desenvolvimento

17 abr, 2019

Conhecendo o Jinja2: um mecanismo para templates no Flask

Publicidade

Neste artigo vou desenvolver uma CRUD utilizando o Flask e o Jinja2 e explicar algumas funcionalidades deles.

O que é Flask e porque ele é considerado um microframework?

Flask — Pocoo

Flask é um microframework para aplicações web utilizando Python. Ele é classificado como microframework porque precisa do mínimo de configuração possível – mantém o core da aplicação simples e deixa você livre para escolher o que usar em sua aplicação (por exemplo, seu banco de dados).

Por default ele não tem camada de abstração de banco de dados, mecanismo de validação de formulário e afins, mas suporta extensões que realizam integrações com banco de dados, requisições, validações de formulários, autenticação, upload de arquivos, entre outras.

Resumindo: você pode utilizar do Flask tudo o que precisa e dispensar tudo o que não precisa no momento.

Jinja2 – Mecanismo de template para Python

Jinja

Jinja é um mecanismo de templates para Python. Ele facilita a tarefa de usar HTML dentro de nossas aplicações Python, tem completo suporte unicode, e para aplicativos que necessitam de maior segurança, adiciona execução em área restrita e escape automático.

A sintaxe dos templates Jinja é semelhante a essa:

http://jinja.pocoo.org/

Seu modo de execução é em sandbox (é semelhante à usar uma máquina virtual, porém, altamente focado na segurança da aplicação, semelhante ao modo privado dos navegadores. Todos os seus aspectos de execução são monitorados e adicionados na lista de permissões ou na lista negra).

É possível utilizar o mesmo layout ou layouts semelhantes para todos os modelos graças ao recurso da herança.

O Jinja traduz as fontes do seu template no primeiro carregamento para o bytecode do Pyhton, just in time na compilação.

Sistema de depuração que integra erros de tempo de execução de modelos e erros de compilação no sistema padrão de rastreamento de erros do Python.

Vamos ao código?

Ao final teremos uma aplicação semelhante a esta – é necessário ter o Python e o Flask instalados:

Primeiramente, vamos criar o server que será o arquivo pokedex.py:

from flask import Flask, render_template, request, redirect, session, flash, url_for
#render template: passando o nome do modelo e a variáveis ele vai renderizar o template
#request: faz as requisições da nosa aplicação
#redirect: redireciona pra outras páginas
#session: armazena informações do usuário
#flash:mensagem de alerta exibida na tela
#url_for: vai para aonde o redirect indica

app = Flask(__name__)
app.secret_key = 'flask'
#chave secreta da sessão

class Pokemon:
    def __init__(self, nome, especie, tipo):
        self.nome = nome
        self.especie = especie
        self.tipo = tipo

class Treinadora:
    def __init__(self, id, nome, senha):
        self.id = id
        self.nome = nome
        self.senha = senha

#criação das terindoras
treinadora1 = Treinadora('Mary', 'Mary Jackson ', '1234')
treinadora2 = Treinadora('Ada', 'Ada Lovelace', '1234')
treinadora3 = Treinadora('Katherine', 'Katherine Johnson', '1234')

treinadoras = {treinadora1.id: treinadora1,
            treinadora2.id: treinadora2,
            treinadora3.id: treinadora3}

#base de dados de pokemons
pokemon1 = Pokemon('Meowth', 'Arranha Gato', 'Normal')
pokemon2 = Pokemon('Charmander', 'Lagarto', 'Fogo')
pokemon3 = Pokemon('Clefairy', 'Fada', 'Fada')
pokemon4 = Pokemon('Machop', 'Superpoder', 'Lutador')
pokemon5 = Pokemon('Rhyhorn', 'Espigão', 'Terrestre/pedra')
pokemon6 = Pokemon('Cyndaquil', 'Rato de fogo', 'Fogo')
pokemon7 = Pokemon('Shuckle', 'Mofo', 'Pedra')
pokemon8 = Pokemon('Whismur', 'Sussuro', 'Normal')
pokemon9 = Pokemon('Swablu', 'Pássaro de algodão', 'Voador')
pokemon10 = Pokemon('Bidoof', 'Rato Gorducho', 'Normal')

lista = [pokemon1, pokemon2,pokemon3,pokemon4,pokemon5,pokemon6,pokemon7,pokemon8,pokemon9,pokemon10]

#configuração da rota index.
@app.route('/')
def index():
    return render_template('lista.html', titulo='Pokedex', pokemons=lista)
    #renderizando o template lista e as variáveis desejadas. 

#configuração da rota novo, ela só poderá ser acessda se o usuário estiver logado, caso contrário redireciona para a tela de login
@app.route('/novo')
def novo():
    if 'usuario_logado' not in session or session['usuario_logado'] == None:
        return redirect(url_for('login', proxima=url_for('novo')))
        return render_template('novo.html', titulo='Novo Pokemon')
        #renderiza o template novo

#configuração da rpta criar que usa o método post para enviar dados dos nossos pokemons
@app.route('/criar', methods=['POST',])
def criar():
    nome = request. form['nome']
    especie = request. form['especie']
    tipo = request. form['tipo']
    pokemon = Pokemon(nome, especie, tipo)
    lista.append(pokemon)
    return redirect(url_for('index'))
#já inclui o novo pokemon na lista e joga na tela inicial

#configuração da rota login
@app.route('/login')
def login():
    proxima = request.args.get('proxima')
    return render_template('login.html', proxima=proxima)

#configuração da rota autenticar que verific as credenciais das terinadoras
@app.route('/autenticar', methods=['POST', ])
def autenticar():
    if request.form['treinadora'] in treinadoras:
        treinadora = treinadoras[request.form['treinadora']]
        if treinadora.senha == request.form['senha']:
            session['usuario_logado'] = treinadora.id
            flash(treinadora.nome + ' acesso permitido!')
            proxima_pagina = request.form['proxima']
            return redirect(proxima_pagina)
    else:
        #caso as credenciais não sejam validadas, exibe mensagem de erro e redirecion para o login
        flash('Acesso negado, digite novamente!')
        return redirect(url_for('login'))

#configuração da rota logout
@app.route('/logout')
def logout():
    session['usuario_logado'] = None
    flash('Treinadora, logue novamente para cadastrar os pokemons que encontrar!')
    return redirect(url_for('index'))


app.run(debug=True)

Agora criaremos os templates que serão renderizados. Crie a pasta templates, e dentro dela crie o arquivo template.html, que será o “pai” dos nossos templates.

Nos templates usaremos o Jinja2, que é o foco deste artigo:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Pokedex</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='bootstrap.css') }}">
  </head>
  <body>
    <div class="container">
        <!--cria container onde as mensagens serão exibidas-->
        {% with messages = get_flashed_messages() %}
            {% if messages %}
            <!--condição para exibição das mensagens-->
                <ul id="messages" class="list-unstyled">
                    <!--laço e repetição para exibição das mensagens-->
                {% for message in messages %}
                    <li class="alert alert-success">{{ message }}</li>
                {% endfor %}
                </ul>
            {% endif %}
        {% endwith %}
                <!--fim dos laços-->
        <div class="page-header">
            <h1>{{ titulo }}</h1>
        </div>
        {% block conteudo %}{% endblock %}
    </div>
    <a class="btn btn-primary" href="/login" role="button">Login </a>
    <a class="btn btn-primary" href="/novo" role="button">Novo </a>
    <a class="btn btn-primary" href="/logout" role="button">Logout </a>


  </body>
</html>

Nesta pasta, também crie o lista.html.

{% extends "template.html" %}
<!--criamos um bloco de conteúdo a ser interpretado pelo python-->
{% block conteudo %}
    <table class="table table-striped table-responsive table-bordered">
        <thead>
            <tr>
                <th>Nome</th>
                <th>Espécie</th>
                <th>Tipo</th>
            </tr>
        </thead>
        <tbody>
            <!--criamos uma estrutura de repetição com o Jinja2 para preencher nossa tabela com os dados dos pokemons-->
            {% for pokemon in pokemons  %}
            
                        <tr>
                            <td>{{ pokemon.nome }}</td>
                            <td>{{ pokemon.especie }}</td>
                            <td>{{ pokemon.tipo }}</td>
                            <!--acessamos os atributos nome, espécie e tipo de cada pokemon-->
                        </tr>
        {% endfor %}
        <!--encerramos a estrutura de repetição-->
        </tbody>
    </table>
{% endblock %}
<!--encerramos o bloco de código a ser interpretado pelo python-->

Nesta mesma pasta criaremos o template login.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Pokedex</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='bootstrap.css') }}">
  </head>
  <body>
    <div class="container">
        <!--criamos um container que vai exibir as mensagens de alerta-->
        {% with messages = get_flashed_messages() %}
            {% if messages %}
            <!--condição para as mensagens-->
                <ul id="messages" class="list-unstyled">
                    <!--criamos um laço de repetição para essas mensagens de alerta-->
                {% for message in messages %}
                    <li class="alert alert-success">{{ message }}</li>
                    <!--chamada da mensagem-->
                {% endfor %}
                <!--fim do for-->
                </ul>
            {% endif %}
            <!--fim do if-->
        {% endwith %}

        <h1>Faça seu login</h1>
        <form method="POST" action="{{ url_for('autenticar') }}">
            <input type="hidden" name="proxima" value="{{ proxima or url_for('index') }}">
            <p><label>Nome da treinadora:</label> <input class="form-control" type="text" name="treinadora" required></p>
            <p><label>Senha:</label> <input class="form-control" type="password" name="senha" required></p>
            <p><button class="btn btn-primary" type="submit">Entrar</button></p>
        </form>
    </div>
</body>
</html>

Agora vamos ao template novo.html:

{% extends "template.html" %}
{% block conteudo %}
    <form action="{{ url_for('criar') }}" method="post">
        <fieldset>
          <div class="form-group">
            <label for="nome">Nome</label>
            <input type="text" id="nome" name="nome" class="form-control">
          </div>
          <div class="form-group">
            <label for="categoria">Espécie</label>
            <input type="text" id="especie" name="especie" class="form-control">
          </div>
          <div class="form-group">
            <label for="console">Tipo</label>
            <input type="text" id="tipo" name="tipo" class="form-control">
          </div>
          <button type="submit" class="btn btn-primary btn-salvar">Registrar na pokedex</button>
        </fieldset>
    </form>
{% endblock %}

O bootstrap deste projeto se encontra aqui. Crie uma pasta na raiz, chamada static, e dentro dela coloque este arquivo bootstrap.css.

Seu projeto deverá estar com uma estrutura semelhante a esta:

Para executar é necessário rodar o comando python pokedex.py.

Referências utilizadas e repositório com o código: