Desenvolvimento Web com Python e Django: Template

Olá sr. Pythonista!

Finalmente chegamos ao nosso último post sobre Django (pelo menos dessa série - com certeza ainda teremos muito conteúdo sobre Django, ok?!)

Primeiramente, quero garantir que você está no ponto certo.

Você já leu os outros posts da série, certo!? Se não, então já se liga:

:ballot_box_with_check: Django: Introdução ao framework
:ballot_box_with_check: Django: A Camada Model
:ballot_box_with_check: Django: A Camada View

E antes de mais nada… Você já está inscrito na nossa exclusiva lista de pythonistas?! Não, então já aproveita a oportunidade!

Agora sim estamos prontos para começar!

Nesse post, vamos tratar da camada que dá a “cara” à nossa aplicação: a Camada Template!

É nela que se encontra o código Python, responsável por renderizar nossas páginas web, e os arquivos HTML, CSS e Javascript que darão vida à nossa aplicação!

Vamos tratar das configurações necessárias para fazer essa camada funcionar corretamente, das ferramentas built-ins do Django para construção de templates, da Django Template Language: sua sintaxe, tags, filters, e muito mais!

Está curioso?

Então VAMOS NESSA!!!

Vá Direto ao Assunto…

Onde estamos…

Primeiro, vamos relembrar onde estamos no fluxo requisição/resposta do nosso servidor Django:

Fluxo de Requisições e Respostas do Django

Ou seja, estamos na camada que faz a interface do nosso código Python/Django com o usuário, interagindo, trocando informações, captando dados de input e gerando dados de output.

Caso você não tenha o código que estamos desenvolvendo nessa séries de posts (o conhecido Hello World), baixe-o aqui!

SPOILER ALERT: Nesse post vamos concentrar nossos esforços em entender a camada de templates para construção de páginas. Nesse momento, não vamos focar na implementação da lógica por trás da Engine de templates, pois acredito que é algo que dificilmente você se verá fazendo, ok?! Se você, mesmo assim, sentir necessidade de uma explicação ou tirar uma dúvida sobre a API de Templates do Django, use o box de comentários ali embaixo para eu saber como te ajudar!

Para relembrar, estamos desenvolvendo um Sistema de Gerenciamento dos Funcionários da sua empresa.

Queremos que, ao final do post, tenhamos páginas customizadas para “mostrar ao seu chefe” seu exímio trabalho! :wink:

Vamos começar pelo começo: o que é um Template?

Definição de Template

Basicamente, um template é um arquivo de texto que pode ser transformado em outro arquivo (um arquivo HTML, um CSS, um CSV, etc…).

Um template contém:

  • Variáveis que podem ser substituídas por valores, a partir do processamento por uma Engine de Templates (núcleo, core, “motor” de templates).
  • Tags que controlam a lógica do template .
  • Filtros que adicionam funcionalidades ao template.

Por exemplo, abaixo está representado um template mínimo que demonstra alguns conceitos básicos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{# base.html contém o template que usaremos como esqueleto #}
{% extends "base.html" %}

{% block conteudo %}
  <h1>{{ section.title }}</h1>

  {% for funcionario in funcionarios %}
    <h2>
      <a href="{% url 'website:funcionario_detalhe' pk=funcionario.id %}">
        {{ funcionario.nome|upper }}
      </a>
    </h2>
  {% endfor %}
{% endblock %}

Alguns pontos importantes:

  • Linha 1: Utilizamos comentário com a tag {# comentário #}.
  • Linha 2: Utilizamos {% extends "base.html" %} para estender de outro template, ou seja, utilizá-lo como base, passando o caminho para ele.
  • Linha 4: Podemos facilitar a organização do template, criando blocos com {% block <nome_do_bloco> %}{% endblock %}.
  • Linha 5: Podemos interpolar código vindo do servidor com o conteúdo do nosso template com {{ secao.titulo }} - dessa forma, estamos acessando o atributo titulo do objeto secao (que deve estar no Contexto da requisição).
  • Linha 7: É possível iterar sobre objetos de uma lista através da tag {% for objeto in lista %}{% endfor %}.
  • Linha 10: Podemos utilizar filtros para aplicar alguma função à algum conteúdo. Nesse exemplo, estamos aplicando o filtro upper, que transforma todos os caracteres da string em maísculos, no conteúdo de funcionario.nome. Também é possível encadear filtros, por exemplo: {{ funcionario.nome|upper|cut:" " }}

Django criou uma linguagem que contém todos esses elementos.

Chamaram-na de DTL - Django Template Language! Veremos mais dela ali embaixo!

Para utilizar os templates do Django, é necessário primeiro configurar sua utilização.

E é isso que veremos agora!

Configuração

Se você já deu uma espiada no nosso arquivo de configurações, o settings.py, você já deve ter visto a seguinte configuração:

1
2
3
4
5
6
7
8
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {},
    },
]

Mas você já se perguntou o que essa configuração quer dizer?

Nela:

  • BACKEND é o caminho para uma classe que implementa a API de templates do Django.
  • DIRS define uma lista de diretórios onde o Django deve procurar por templates. A ordem da lista define a ordem de busca.
  • APP_DIRS define se o Django deve procurar por templates dentro dos diretórios dos apps instalados em INSTALLED_APPS.
  • OPTIONS contém configurações específicas do BACKEND escolhido, ou seja, dependendo do backend de templates que você escolher, você poderá configurá-lo utilizando o OPTIONS.

Por ora, vamos utilizar as configurações padrão “de fábrica” pois elas já nos atendem!

Agora, vamos ver sobre a tal Django Template Language!

Django Template Language

A DTL é a linguagem padrão de templates do Django. Ela é simples, porém poderosa.

Dando uma olhada na sua documentação, podemos ver a filosofia da DTL (traduzido):

Se você tem alguma experiência em programação, ou se você está acostumado com linguagens que misturam código de programação diretamente no HTML, você deve ter em mente que o sistema de templates do Django não é simplesmente código Python embutido no HTML. Isto é: o sistema de templates foi desenhado para ser a apresentação, e não para conter lógica!

Se você vem de outra linguagem de programação deve ter tido contato com o seguinte tipo de construção: código de programação adicionado diretamente no código HTML (como PHP).

Isto é o terror dos designers!

Ponha-se no lugar de um designer que não sabe nada sobre programação. Agora imagina você tendo que dar manutenção nos estilos de uma página LOTADA de código de programação?!

Complicado, hein?! :dizzy_face:

Agora, nada melhor para aprender sobre a DTL do que botando a mão na massa e melhorando as páginas da nossa aplicação, né?!

Primeiro, vamos começar fazendo algumas alterações na estrutura do nosso projeto!

Observação: nesse post eu vou utilizar o Bootstrap 4 para dar um “tapa no visual”. Se surgir alguma dúvida, não se esqueça do box de comentário ao final do post, ok?!

Alterações no projeto

Para dar continuidade no nosso projeto, vamos fazer as seguintes alterações:

  • Crie a pasta _layouts dentro de website/templates/website a fim de separar os templates-base (layouts) da nossa aplicação.
  • Crie o arquivo base.html dentro de /layouts.
  • Crie a pasta static dentro de website para guardar os arquivos estáticos que vamos utilizar (arquivos de estilo CSS, arquivos Javascript, imagens, fontes, etc…). Crie também a pasta website dentro dela.
  • Dentro dessa pasta (/static/website/), crie as pastas css, img e js (arquivos .css, imagens e Javascript).
  • Adicione o parâmetro name='' às URLs no arquivo website/urls.py, da seguinte forma:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
urlpatterns = [
    # '/'
    path('', 
      IndexTemplateView.as_view(), 
      name="index" # <<< Adicionar
    ),

    # '/funcionario/cadastrar'
    path('funcionario/cadastrar', 
      FuncionarioCreateView.as_view(), 
      name="cadastra_funcionario" # <<< Adicionar
    ),

    # '/funcionarios'
    path('funcionarios/', 
      FuncionarioListView.as_view(), 
      name="lista_funcionarios" # <<< Adicionar
    ),

    # '/funcionario/{pk}'
    path('funcionario/<pk>', 
      FuncionarioUpdateView.as_view(), 
      name="atualiza_funcionario" # <<< Adicionar
    ),

    # '/funcionarios/excluir/{pk}'
    path('funcionario/excluir/<pk>', 
      FuncionarioDeleteView.as_view(), 
      name="deleta_funcionario" # <<< Adicionar
    ),
]

Observação: Nós criamos uma pasta com o nome do app (website, no caso) dentro das pastas static e templates para que o Django crie o namespace do app. Dessa forma, o Django entende onde buscar os recursos quando você precisar!

Dessa forma, devemos estar com a estrutura da seguinte forma:

Estrutura do projeto

Com a estrutura acima, vamos começar pelo template-base do app website!

Template-base

Nosso template que servirá de esqueleto deve conter o código HTML que irá se repetir em todas as páginas.

Devemos colocar nele os trechos de código mais comuns das páginas HTML.

Por exemplo, toda página HTML:

  • Deve ter as tags: <html></html>, <head></head> e <body></body>.
  • Deve ter os links para os arquivos estáticos: <link></link> e <script></script>.
  • Quaisquer outros trechos de código que se repitam em nossas páginas.

Você pode fazer o download dos arquivos necessários para o nosso projeto aqui (Bootstrap) e aqui (jQuery), que é uma dependência do Bootstrap, ou utilizar os arquivos que eu já baixei e estão na pasta website/static/.

Faça isso para todos as bibliotecas externas que queira utilizar (ou utilize um CDN - Content Delivery Network).

Ok! Agora, com os arquivos devidamente colocados na pasta /static/, podemos começar com:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!DOCTYPE html>
<html>
  {% load static %}
  <head>
    <!-- Title -->
    <title>
      {% block title %}Sistema de Gerenciamento de Funcionários{% endblock %}
    </title>

    <!-- Favicon -->
    <link rel="shortcut icon" href="{% static 'website/img/favicon.png' %}" type="image/png">

    <!-- Arquivos CSS -->
    <link rel="stylesheet" href="{% static 'website/css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'website/css/master.css' %}">

    <!-- Bloco de Estilos -->
    {% block styles %}{% endblock %}
  </head>

  <body>
    <!-- Navbar -->
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="{% url 'website:index' %}">
          <img src="{% static 'website/img/favicon.png' %}" height='45px'>
        </a>
        <button class="navbar-toggler" type="button" 
            data-toggle="collapse" data-target="#conteudo-navbar" 
            aria-controls="conteudo-navbar" aria-expanded="false" 
            aria-label="Ativar navegação">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="conteudo-navbar">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="{% url 'website:index' %}">
                      Página Inicial
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="{% url 'website:lista_funcionario' %}">
                      Funcionários
                    </a>
                </li>
            </ul>           
        </div>
    </nav>
      
    <!-- Bloco de conteúdo -->
    {% block conteudo %}{% endblock %}

    <!-- Arquivos necessários para o Bootstrap -->
    <script src="{% static 'website/js/jquery.min.js' %}"></script>
    <script src="{% static 'website/js/bootstrap.min.js' %}"></script>

    <!-- Bloco de scripts -->
    {% block scripts %}{% endblock %}

    <!-- Scripts.js -->
    <script src="{% static 'website/js/scripts.js' %}"></script>
  </body>
</html>

E vamos as explicações:

  • <!DOCTYPE html> serve para informar ao browser do usuário que se trata de uma página HTML5.
  • Para que o Django possa carregar dinamicamente os arquivos estáticos do site, utilizamos a tag static. Ela vai fazer a busca do arquivo que você quer e fazer a conversão dos links corretamente. Para utilizá-la, é necessário primeiro carregá-la. Fazemos isso com {% load <modulo> %}. Após seu carregamento, utilizamos a tag {% static 'caminho/para/arquivo' %}, passando como parâmetro a localização relativa à pasta /static/.
  • Podemos definir quaisquer blocos no nosso template com a tag {% block nome_do_bloco %}{% endblock %}. Fazemos isso para organizar melhor as páginas que irão estender desse template. Podemos passar um valor padrão dentro do bloco (igual está sendo utilizado na linha 6) - dessa forma caso não seja definido nenhum valor no template filho - é aplicado o valor padrão.
  • Colocamos nesse template os arquivos necessários para o funcionamento do Bootstrap, isto é: o jQuery, o CSS e Javascript do Bootstrap.
  • O link para outras páginas da nossa aplicação é feito utilizando-se a tag {% url 'nome_da_view' parametros... %}. Dessa forma, deixamos que o Django cuide da conversão para URLs válidas!
  • O conjunto de tags <nav></nav> definem a barra superior de navegação com os links para as páginas da aplicação. Esse também é um trecho de código presente em todas as páginas, por isso, adicionamos ao template. (Documentação da Navbar - Bootstrap)

E pronto! Temos um template base!

Antes de continuar… Está gostando do conteúdo mas ainda não faz parte da nossa exclusiva lista de pythonistas viciados em conteúdo de qualidade?

Não? Então já aproveita!!! :point_down: :point_down: :point_down:

Agora, vamos customizar a tela principal da nossa aplicação: a index.html!

Página Inicial

Template: website/index.html

Nossa tela inicial tem o objetivo de apenas mostrar as opções disponíveis ao usuário, que são:

  • Link para a página de cadastro de novos Funcionários.
  • Link para a página de listagem de Funcionários.

Primeiramente, precisamos dizer ao Django que queremos utilizar o template que definimos acima como base.

Para isso, utilizamos a tag {% extends "caminho/para/template" %}, que serve para que um template estenda de outro.

Com isso, podemos fazer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!-- Estendemos do template base -->
{% extends "website/_layouts/base.html" %}

<!-- Bloco que define o <title></title> da nossa página -->
{% block title %}Página Inicial{% endblock %}

<!-- Bloco de conteúdo da nossa página -->    
{% block conteudo %}
<div class="container">
  <div class="row">
    <div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
      <div class="card">
        <div class="card-body">
          <h5 class="card-title">Cadastrar Funcionário</h5>
          <p class="card-text">
            Cadastre aqui um novo <code>Funcionário</code>.
          </p>
          <a href="{% url 'website:cadastra_funcionario' %}"
             class="btn btn-primary">
            Novo Funcionário
          </a>
        </div>
      </div>
    </div>
    <div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
      <div class="card">
        <div class="card-body">
          <h5 class="card-title">Lista de Funcionários</h5>
          <p class="card-text">
            Veja aqui a lista de <code>Funcionários</code> cadastrados.
          </p>
          <a href="{% url 'website:lista_funcionarios' %}"
             class="btn btn-primary">
            Vá para Lista
          </a>
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Nesse template:

  • A classe container do Bootstrap (linha 9) serve para definir a área útil da nossa página (para que nossa página fique centralizada e não fique ocupando todo o comprimento da página).
  • As classes row e col-* fazem parte do sistema Grid do Bootstrap e servem para tornar nossa página responsiva (que se adapta aos diversos tipos e tamanhos de tela: celular, tablet, desktop etc…).
  • As classes card* fazem parte do component Card do Bootstrap.
  • As classes btn e btn-primary (documentação) são usados para dar o visual de botão à algum elemento.

Com isso, nossa Página Inicial - ou nossa Homepage - deve ficar assim:

Página Inicial

Top, hein?! :ok_hand:

Agora vamos para a página de cadastro de Funcionários: a cria.html

Template de Cadastro de Funcionários

Template: website/cria.html

Nesse template, mostramos o formulário para cadastro de novos funcionários.

Se lembra que definimos o formulário InsereFuncionarioForm no post passado?

Pois então… Dessa forma, nossa View FuncionarioCreateView expõe um objeto form no nosso template para que possamos utilizá-lo.

Mas antes de seguir, vamos instalar uma biblioteca que nos ajuda e muito a renderizar os campos de input do nosso formulário: a Widget Tweaks!

Com ela, nós temos maior liberdade para customizar os campos de input do nosso formulário (adicionando classes CSS e/ou atributos, por exemplo).

Para isso, primeiro nós a instalamos com:

1
pip install django-widget-tweaks

Depois a adicionamos a lista de apps instalados, no arquivo helloworld/settings.py:

1
2
3
4
5
INSTALLED_APPS = [
    ...
    'widget_tweaks',
    ...
]

E, no template onde formos utilizá-lo, carregamos ela com {% load widget_tweaks %}!

E pronto, podemos utilizar a tag {% render_field nome_do_campo parâmetros %} passando parâmetros, configurando a forma como o input será renderizado!

Assim, nós podemos construir nosso template da seguinte forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
{% extends "website/_layouts/base.html" %}

{% load widget_tweaks %}

{% block title %}Cadastro de Funcionários{% endblock %}

{% block conteudo %}
<div class="container">
  <div class="row">
    <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
      <div class="card">
        <div class="card-body">
          <h5 class="card-title">Cadastro de Funcionário</h5>
          <p class="card-text">
            Complete o formulário abaixo para cadastrar
            um novo <code>Funcionário</code>.
          </p>
          <form method="post">
            <!-- Não se esqueça dessa tag -->
            {% csrf_token %}

            <!-- Nome -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">Nome</span>
              </div>
              {% render_field form.nome class+="form-control" %}
            </div>

            <!-- Sobrenome -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">Sobrenome</span>
              </div>
              {% render_field form.sobrenome class+="form-control" %}
            </div>

            <!-- CPF -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">CPF</span>
              </div>
              {% render_field form.cpf class+="form-control" %}
            </div>

            <!-- Tempo de Serviço -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">Tempo de Serviço</span>
              </div>
              {% render_field form.tempo_de_servico class+="form-control" %}
            </div>

            <!-- Remuneração -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">Remuneração</span>
              </div>
              {% render_field form.remuneracao class+="form-control" %}
            </div>

            <button class="btn btn-primary">Enviar</button>
          </form>
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Aqui:

  • Utilizamos, novamente as classes container, row, col-* e card* do Bootstrap.
  • Conforme mencionei no post passado, devemos adicionar a tag {% csrf_token %} para evitar ataques de Cross Site Request Forgery.
  • As classes Input Group do Bootstrap input-group, input-group-prepend e input-group-text servem para customizar o estilo do <input ... />.
  • Utilizamos o {% render_field form.campo class+='form-control' %} para aplicar a classe form-control do Bootstrap ao input do formulário.

Observação: É possível adicionar a classe CSS form-control diretamente no nosso InsereFuncionarioForm, da seguinte forma:

1
2
3
4
5
6
7
8
9
10
11
class InsereFuncionarioForm(forms.ModelForm):
  ...
  nome = forms.CharField(
    max_length=255,
    widget=forms.TextInput(
      attrs={
        'class': "form-control"
      }
    )
  )
  ...

Mas eu não aconselho fazer isso, pois deixa nosso código extremamente acoplado. Veja que para mudar a classe CSS (atributo da interface) teremos que mudar código Python (backend). Por isso, aconselho a utilização de ferramentas para tal, como o Widget Tweaks, pois alteramos código apenas no template!

Com isso, nosso formulário deve ficar assim:

Página de Cadastro de Funcionários

Agora, vamos desenvolver o template de listagem de Funcionários.

Template de Listagem de Funcionários

Template: website/lista.html

Nessa página, nós queremos mostrar o conjunto de Funcionários cadastrado no banco de dados e as ações que o usuário pode tomar: atualizar o Funcionário ou excluí-lo.

Se lembra da view FuncionarioListView?

Ela é responsável por buscar a lista de Funcionários e expor um objeto chamado funcionarios para iteração no template.

Podemos construir nosso template da seguinte forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
{% extends "website/_layouts/base.html" %}

{% block title %}Lista de Funcionários{% endblock %}

{% block conteudo %}
<div class="container">
  <div class="row">
    <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
      <div class="card">
        <div class="card-body">
          <h5 class="card-title">Lista de Funcionário</h5>

          {% if funcionarios|length > 0 %}
            <p class="card-text">
              Aqui está a lista de <code>Funcionários</code> cadastrados.
            </p>

            <table class="table">
              <thead class="thead-dark">
              <tr>
                <th>ID</th>
                <th>Nome</th>
                <th>Sobrenome</th>
                <th>Tempo de Serviço</th>
                <th>Remuneração</th>
                <th>Ações</th>
              </tr>
              </thead>

              <tbody>
              {% for funcionario in funcionarios %}
                <tr>
                  <td>{{ funcionario.id }}</td>
                  <td>{{ funcionario.nome }}</td>
                  <td>{{ funcionario.sobrenome }}</td>
                  <td>{{ funcionario.tempo_de_servico }}</td>
                  <td>{{ funcionario.remuneracao }}</td>
                  <td>
                    <a href="{% url 'website:atualiza_funcionario' pk=funcionario.id %}"
                       class="btn btn-info">
                      Atualizar
                    </a>
                    <a href="{% url 'website:deleta_funcionario' pk=funcionario.id %}"
                      class="btn btn-outline-danger">
                        Excluir
                    </a>
                  </td>
                </tr>
              {% endfor %}
              </tbody>
            </table>
        {% else %}
          <div class="text-center mt-5 mb-5 jumbotron">
            <h5>Nenhum <code>Funcionário</code> cadastrado ainda.</h5>
          </div>
        {% endif %}
          <hr />
          <div class="text-right">
            <a href="{% url 'website:cadastra_funcionario' %}" class="btn btn-primary">
              Cadastrar Funcionário
            </a>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Nesse template:

  • Utilizamos classes do Bootstrap para estilizar as tabelas. São elas: table para estilizar o cabeçalho e as linhas e thead-dark para escurecer o cabeçalho.
  • Na linha 13, utilizamos o filtro length para verificar se a lista de funcionários está vazia. Se ela tiver dados, é mostrada a tabela. Se estiver vazia, é mostrado o componente Jumbotron do Bootstrap com o texto “Nenhum Funcionário cadastrado ainda”.
  • Na linha 30 utilizamos a tag {% for funcionario in funcionarios %} para iterar sobre a lista funcionarios.
  • Nas linhas 39 e 46 fazemos o link para as páginas de atualização e exclusão do usuário.

O resultado, sem Funcionários cadastrados, deve ser esse:

Página de Listagem de Funcionários - sem cadastros

E com um Funcionário cadastrado:

Página de Listagem de Funcionários - com cadastros

Quando o usuário clicar em “Excluir”, ele será levado para a página exclui.html e quando clicar em “Atualizar”, ele será levado para a página atualiza.html.

Vamos começar construindo a página de Atualização de Funcionários!

Template de Atualização de Funcionários

Template: website/atualiza.html

Nessa página, queremos que o usuário possa ver os dados atuais do Funcionário e possa atualizá-los, conforme sua vontade.

Para isso utilizamos a view FuncionarioUpdateView implementada no post passado.

Ela expõe um formulário com os dados do modelo previamente preenchidos para que o usuário possa alterar.

Vamos utilizar novamente a biblioteca Widget Tweaks para facilitar a renderização dos campos de input.

Abaixo, como podemos fazer nosso template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{% extends "website/_layouts/base.html" %}

{% load widget_tweaks %}

{% block title %}Atualização de Dados do Funcionários{% endblock %}

{% block conteudo %}
<div class="container">
  <div class="row">
    <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
      <div class="card">
        <div class="card-body">
          <h5 class="card-title">Atualização de Dados do Funcionário</h5>          
          <form method="post">
            <!-- Não se esqueça dessa tag -->
            {% csrf_token %}

            <!-- Nome -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">Nome</span>
              </div>
              {% render_field form.nome class+="form-control" %}
            </div>

            <!-- Sobrenome -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">Sobrenome</span>
              </div>
              {% render_field form.sobrenome class+="form-control" %}
            </div>

            <!-- CPF -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">CPF</span>
              </div>
              {% render_field form.cpf class+="form-control" %}
            </div>

            <!-- Tempo de Serviço -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">Tempo de Serviço</span>
              </div>
              {% render_field form.tempo_de_servico class+="form-control" %}
            </div>

            <!-- Remuneração -->
            <div class="input-group mb-3">
              <div class="input-group-prepend">
                <span class="input-group-text">Remuneração</span>
              </div>
              {% render_field form.remuneracao class+="form-control" %}
            </div>

            <button class="btn btn-primary">Enviar</button>
          </form>
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Nesse template, não temos nada de novo. Perceba que seu código é similar ao template de adição de Funcionários.

Sua interface fica bem similar também:

Página de Atualização de Funcionários

E por último, temos o template de exclusão de Funcionários.

Template de Exclusão de Funcionários

Template: website/exclui.html

A função dessa página é mostrar uma página de confirmação para o usuário antes da exclusão de um Funcionário.

A view que fizemos, a FuncionarioDeleteView, facilita bastante nossa vida. Com ela, basta dispararmos uma requisição POST para a URL de exclusão!

Dessa forma, nosso objetivo se resume à:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- Estendemos do template base -->
{% extends "website/_layouts/base.html" %}

<!-- Bloco que define o <title></title> da nossa página -->
{% block title %}Página Inicial{% endblock %}

<!-- Bloco de conteúdo da nossa página -->
{% block conteudo %}
  <div class="container mt-5">
    <div class="card">
      <div class="card-body">
        <h5 class="card-title">Exclusão de Funcionário</h5>

        <p class="card-text">
          Você tem certeza que quer excluir o funcionário 
          <b>{{ funcionario.nome }}</b>?
        </p>

        <form method="post">
          {% csrf_token %}

          <hr />
          <div class="text-right">
            <a href="{% url 'website:lista_funcionarios' %}" 
              class="btn btn-outline-danger">
                Cancelar
            </a>
            <button class="btn btn-danger">Excluir</button>
          </div>
        </form>
      </div>
    </div>
  </div>
{% endblock %}

Aqui, nada de novo também.

Apenas mostramos o formulário onde o usuário pode decidir excluir ou não o Funcionário, que deve ficar assim:

Página de Exclusão de Funcionários

Pronto!

Com isso, temos todas as telas do nosso projeto! :smile:

Agora vamos ver como construir tags e filtros customizados!

Tags e Filtros customizados

Sabemos, agora, que o Django possui uma grande variedade de filtros e tags pré-configurados.

Contudo, é possível que, em alguma situação específica, o Django não te ofereça o filtro ou tag necessário.

Para isso, ele previu a possibilidade de você construir seus próprios filtros e tags!

E é exatamente isso que veremos agora!

Vamos construir uma tag que irá nos dizer o tempo atual formatado e um filtro que irá retornar a primeira letra da string passada.

Para isso, vamos começar com a configuração necessária!

Configuração

Para utilizar tags e filtros customizados, precisamos criar uma pasta chamada /templatetags na raíz do app.

Crie a pasta website/templatetags/ e dentro dela, adicione:

  • Um script __init__.py em branco
  • O script tempo_atual.py referente à nossa tag
  • O script primeira_letra.py referente ao nosso filtro.

Nossa estrutura deve ficar:

1
2
3
4
5
6
7
website/
   ...
   templatetags/
      __init__.py
      tempo_atual.py
      primeira_letra.py
  ...

Para que o Django enxergue nossas tags e filtros é necessário que o app onde eles estão instalada esteja configurada na lista INSTALLED_APPD do settings.py (no nosso caso, website já está lá, portanto, nada a fazer aqui).

Também é necessário carregá-los com o {% load filtro/tag %}.

Vamos começar com o filtro.

Vamos chamá-lo de primeira_letra e iremos utilizá-lo da seguinte maneira:

1
<p>{{ valor|primeira_letra }}</p>

Filtro primeira_letra

Filtros customizados são apenas funções que recebem um ou dois argumentos. São eles:

  • O valor do input.
  • O valor do argumento - que pode ter um valor padrão ou não receber nenhum valor.

No nosso filtro {{ valor|primeira_letra }}:

  • valor é o input value.
  • Nosso filtro não receberá argumentos, portanto não foi passado nada como tal.

Para ser um filtro válido, é necessário que o código dele contenha uma variável chamada register que seja uma instância de template.Library (onde todos os tags e filtros são registrado).

Isso define um filtro.

Outra questão são as Exceções.

Como a engine de templates do Django não provê tratamento de exceção à execução do filtro, qualquer exceção no filtro será exposta como uma exceção do próprio servidor.

Por isso, nosso filtro deve evitar lançar exceções e, ao invés disso, deve retornar um valor padrão.

Vamos ver um exemplo de filtro do Django.

Abra o arquivo django/template/defaultfilter.py. Lá temos o exemplo do filtro lower:

1
2
3
4
5
@register.filter(is_safe=True)
@stringfilter
def lower(value):
    """Convert a string into all lowercase."""
    return value.lower()

Nele:

  • @register.filter(is_safe=True) é utilizado para registrar sua função *como um filtro para o Django. Só assim o framework vai enxergar seu código.
  • @stringfilter é um decorator utilizado para dizer ao Django que seu filtro espera uma string como argumento (saiba mais sobre decorators no nosso post Domine Decorators em Python).

Com essas informações, vamos agora codificar e registrar nosso próprio filtro!

Uma forma de pegarmos a primeira letra de uma string é transformá-la em lista e pegar o elemento de índice 0, da seguinte forma:

1
2
3
4
5
6
7
8
9
from django import template
from django.template.defaultfilters import stringfilter

register = template.Library()

@register.filter
@stringfilter
def primeira_letra(value):
    return list(value)[0]

Nesse código:

  • O código register = template.Library() é necessário para registrarmos nosso filtro no catálogo de filtros do Django.
  • @register.filter e @stringfilter são os decorators que citei aqui em cima.

E agora vamos fazer o teste em algum template nosso.

Vamos alterar nossa tabela no template website/lista.html para incluir nosso filtro da seguinte forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<table class="table">
  <thead class="thead-dark">
    <tr>    
      <th><!-- Retiramos o "ID" aqui --></th>
      <th>Nome</th>
      <th>Sobrenome</th>
      <th>Tempo de Serviço</th>
      <th>Remuneração</th>
      <th class="text-center">Ações</th>
    </tr>
  </thead>
  <tbody>
    {% for funcionario in funcionarios %}
      <tr>
        <!-- Aplicamos nosso filtro no atributo funcionario.nome -->
        <td>{{ funcionario.nome|primeira_letra }}</td>
        <td>{{ funcionario.nome }}</td>
        <td>{{ funcionario.sobrenome }}</td>
        <td>{{ funcionario.tempo_de_servico }}</td>
        <td>{{ funcionario.remuneracao }}</td>
        <td class="text-center">
          <a href="{% url 'website:atualiza_funcionario' pk=funcionario.id %}"
              class="btn btn-primary">
            Atualizar
          </a>
          <a href="{% url 'website:deleta_funcionario' pk=funcionario.id %}"
            class="btn btn-danger">
              Excluir
          </a>
        </td>
      </tr>
    {% endfor %}
  </tbody>
</table>

O que resulta em:

_Template_ alterado com a inclusão do nosso filtro customizado

E com isso, fizemos nosso primeiro filtro!

Agora vamos fazer nossa tag customizada: a tempo_atual!

Tag tempo_atual

De acordo com a documentação do Django, “tags são mais complexas que filtros pois podem fazer qualquer coisa.

Desenvolver uma tag pode ser algo bem trabalhoso, dependendo do que você deseja fazer. Mas também pode ser simples.

Como nossa tag vai apenas mostrar o tempo atual, sua implementação não deve ser complexa.

Para isso, utilizaremos um “atalho” do Django: a simple_tag!

A simple_tag é uma ferramenta para construção de tags simples (assim como o próprio nome já diz).

Com ela, a criação de tags fica similar à criação de filtros, que vimos na seção passada.

Precisamos incluir uma instância de template.Library, utilizar o decorator @register e definir nossa função.

Dessa forma, podemos definí-la como:

1
2
3
4
5
6
7
8
import datetime
from django import template

register = template.Library()

@register.simple_tag
def tempo_atual():
    return datetime.datetime.now().strftime('%H:%M:%S')

E para utilizá-la, fazemos o carregamento com {% load tempo_atual %} e utilizamos em nosso template com {% tempo_atual %}.

No nosso caso, vamos alterar o arquivo website/_layouts/base.html para utilizar nossa tag.

Vamos adicionar um novo item à barra de navegação (do lado direito), da seguinte forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<body>
  <!-- Navbar -->
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
      ...
      <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <ul class="navbar-nav mr-auto">
              <li class="nav-item active">
                  <a class="nav-link" 
                    href="{% url 'website:index' %}">
                      Página Inicial
                  </a>
              </li>
              <li class="nav-item">
                  <a class="nav-link" 
                    href="{% url 'website:lista_funcionarios' %}">
                      Funcionários
                  </a>
              </li>
          </ul>
          <!-- Adicione a lista abaixo -->
          <ul class="navbar-nav float-right">
              <li class="nav-item">
                  <!-- Aqui está nosso filtro -->
                  <a class="nav-link" href="#">
                    <b>Hora: </b>{% tempo_atual %}
                  </a>
              </li>
          </ul>
      </div>
  </nav>
  ...

O resultado deve ser:

_Tag_ customizada em ação

Com isso, temos nosso filtro e tag customizados!

Agora vamos dar uma olhada no filtros que estão presentes no próprio Django: os Built-in Filters!

Built-in Filters

É possível fazer muita coisa com os filtros que já veem instalados no próprio Django!

Muitas vezes, é melhor você fazer algumas operações no template do que fazê-las no backend. Sempre verifique a viabilidade de um ou de outro para facilitar sua vida!

Como a lista de built-in filters do Django é bem extensa (veja a lista completa aqui), vou listar aqui os que eu considero mais úteis!

Use essa página como referência!

Favorite-a se quiser para ter acesso rápido à esse conteúdo!

Sem mais delongas, aí vai o primeiro: o capfirst!!!

Filtro capfirst

O que faz: Torna o primeiro caracter do valor para maiúsculo.

Exemplo:

Entrada: valor = 'esse é um texto'.

Utilização:

1
{{ valor|capfirst }}

Saída:

1
Esse é um texto

Filtro cut

O que faz: Remove todas as ocorrências do parâmetro no valor passado.

Exemplo:

Entrada: valor = 'Esse É Um Texto De Testes'

Utilização:

1
{{ valor|cut:" " }}

Saída:

1
EsseÉUmTextoDeTestes

Filtro date

O que faz: Utilizado para formatar datas. Possui uma grande variedade de configurações (veja aqui).

Exemplo:

Entrada: Objeto datetime.

Utilização:

1
{{ data|date:'d/m/Y' }}

Saída:

1
01/07/2018

Filtro default

O que faz: Caso o valor seja False, utiliza o valor default.

Exemplo:

Entrada: valor = False

Utilização:

1
{{ valor|default:'Nenhum valor' }}

Saída:

1
Nenhum valor

Filtro default_if_none

O que faz: Similar ao filtro default, caso o valor seja None, utiliza o valor configurado em default_if_none.

Exemplo:

Entrada: valor = None

Utilização:

1
{{ valor|default:'Nenhum valor' }}

Saída:

1
Nenhum valor

Filtro divisibleby

O que faz: Retorna True se o valor for divisível pelo argumento.

Exemplo:

Entrada: valor = 14 e divisibleby:'2'

Utilização:

1
{{ valor|divisibleby:'2' }}

Saída:

1
True

Filtro filesizeformat

O que faz: Transforma tamanhos de arquivos em valores legíveis por humanos.

Exemplo:

Entrada: valor = 123456789

Utilização:

1
{{ valor|filesizeformat }}

Saída:

1
117.7 MB

Filtro first

O que faz: Retorna o primeiro item em uma lista

Exemplo:

Entrada: valor = ["Marcos", "João", "Luiz"]

Utilização:

1
{{ valor|first }}

Saída:

1
Marcos

Filtro last

O que faz: Retorna o último item em uma lista

Exemplo:

Entrada: valor = ["Marcos", "João", "Luiz"]

Utilização:

1
{{ valor|last }}

Saída:

1
Luiz

Filtro floatformat

O que faz: Arredonda números com ponto flutuante com o número de casas decimais passado por argumento.

Exemplo:

Entrada: valor = 14.25145

Utilização:

1
{{ valor|floatformat:"2" }}

Saída:

1
14.25

Filtro join

O que faz: Junta uma lista utilizando a string passada como argumento como separador.

Exemplo:

Entrada: valor = ["Marcos", "João", "Luiz"]

Utilização:

1
{{ valor|join:" - " }}

Saída:

1
Marcos - João  - Luiz

Filtro length

O que faz: Retorna o comprimento de uma lista ou string. É muito utilizado para saber se existem valores na lista.

Exemplo:

Entrada: valor = ['Marcos', 'João']

Utilização:

1
2
3
4
5
{% if valor|length > 0 %}
<p>Lista contém valores</p>
{% else %}
<p>Lista vazia</p>
{% endif %}

Saída:

1
<p>Lista contém valores</p>

Filtro lower

O que faz: Transforma todos os caracteres de uma string em minúsculas.

Exemplo:

Entrada: valor = PaRaLeLePíPeDo

Utilização:

1
{{ valor|lower }}

Saída:

1
paralelepípedo

Filtro pluralize

O que faz: Retorna um sufixo plural caso o número seja maior que 1.

Exemplo:

Entrada: valor = 12

Utilização:

1
Sua empresa tem {{ valor }} Funcionário{{ valor|pluralize:"s" }}

Saída:

1
Sua empresa tem 12 Funcionários

Filtro random

O que faz: Retorna um item aleatório de uma lista

Exemplo:

Entrada: valor = [1, 2, 3, 4, 5, 6, 7, 9]

Utilização:

1
{{ valor|random }}

Sua saída será um valor da lista escolhido randomicamente.


Filtro title

O que faz: Transforma em maísculo o primeiro caracter de todas as palavras do texto.

Exemplo:

Entrada: valor = 'Esse é o primeiro post do blog'

Utilização:

1
{{ valor|title }}

Saída:

1
Esse é O Primeiro Post Do Blog

Filtro upper

O que faz: Transforma em maísculo todos caracteres da string.

Exemplo:

Entrada: valor = texto de testes

Utilização:

1
{{ valor|upper }}

Saída:

1
TEXTO DE TESTES

Filtro wordcount

O que faz: Retorna o número de palavras da string.

Exemplo:

Entrada: valor = Django é o melhor framework web

Utilização:

1
{{ valor|wordcount }}

Saída:

1
6

Bom…

Eu acho que está bom de filtros por aqui! :satisfied:

Código

O código completo desenvolvido nesse projeto está disponível no Github da Python Academy. Clique aqui para acessá-lo e baixá-lo!

Para rodar o projeto, execute em seu terminal:

  • pip install -r requirements.txt para instalar as dependências.
  • python manage.py makemigrations para criar as Migrações.
  • python manage.py migrate para efetivar as Migrações no banco de dados.
  • python manage.py runserver para executar o servidor de testes do Django.
  • Acessar o seu navegador na página http://localhost:8000 (por padrão).

E pronto… Servidor rodando! :wink:

Conclusão

Ufa… Por hoje é só!

Nesse post vimos como configurar, customizar e estender templates, como utilizar os filtros e tags do Django, como criar tags e filtros customizados e um pouquinho de Bootstrap, pra deixar as páginas bonitonas!

E com isso, senhores Pythonistas, terminamos nossa sequência de posts baseados em Django!

Espero que tenham gostado do conteúdo… Tentei abordar a maior quantidade de conhecimento aqui para que você possa iniciar sua jornada no Django com o pé direito!

Creio que o conhecimento abordado aqui é suficiente para vocês iniciarem o desenvolvimento de projetos pessoais e até profissionais utilizando Django!

Claro que ainda há muito para aprender, mas vimos bastante coisa por aqui…

E você… Como se saiu?!

Que tal customizar, modificar e deixar esse projeto um brinco?!

Faça e poste o resultado aqui embaixo no box de comentários!

Quero ver como você se saiu no seu primeiro projeto Django! :wink:

É isso pessoal!

Até a próxima!

Gostou do conteúdo? Compartilha aí!