• Nenhum resultado encontrado

Configuração de arquivos estáticos

No documento Python e Django Fundamentos (páginas 191-199)

A pasta de arquivos estáticos do projeto pode ser criada na pasta onde ficam as configu- rações do projeto, onde também está o arquivo settings.py. O mais importante é definir a variável STATICFILES_DIRS.

STATICFILES_DIRS = (

os.path.join(BASE_DIR, 'umas_e_ostras/static'), )

Foi usado o método os.path.join para unir a variável BASE_DIR e o nome da pasta “umas_e_ ostras/static”. Essa técnica é importante, pois quando esse projeto for implantado em um servidor, não será necessário alterar essa configuração. BASE_DIR terá o caminho do projeto e será concatenado com o nome da subpasta onde os arquivos estáticos serão armazenados. Os templates da aplicação reservas precisam ser alterados para utilizar o template do projeto. A primeira linha deve ter o comando para estender o template base. O conteúdo precisa ser gerado dentro de um bloco, chamado de conteúdo.

{% extends 'base.html' %}

... Qualquer html preexistente que não faz parte do conteúdo. {% block conteudo %}

... código preexistente nos templates ... {% endblock %}

Isso precisa ser aplicado a todos os templates do projeto. Em nosso exemplo, são os tem- plates da aplicação reservas, já que o projeto só tem uma aplicação.

reservas/templates/reservas/index.html

O template index.html da aplicação reservas serve como exemplo para explicar o funciona- mento da capacidade de estender templates.

{% extends 'base.html' %} {% load staticfiles %}

<link rel="stylesheet" type="text/css" href="{% static 'reservas/style.css' %}" /> {% block conteudo %}

{% if ultimos_clientes %} <ul>

Py th on e D ja ng o F un da m en to s <li>

<a href="{% url 'reservas:detalhe' cliente.id %}"> {{ cliente.nome }} </a> </li> {% endfor %} </ul> {% else %}

<p>Nenhum cliente registrado.</p> {% endif %}

{% endblock %}

O trecho antigo mantido fora do bloco conteudo não causa mais efeito algum, pois não está contido em nenhum bloco e é ignorado pelo sistema de templates.

{% load staticfiles %}

<link rel="stylesheet" type="text/css" href="{% static ‘reservas/style.css’ %}" />

Só aquilo que está no bloco conteudo será apresentado ao acessar a view que utiliza esse template.

reservas/templates/reservas/lista.html

O template lista.html teve a adição do cabeçalho com o comando para estender o template básico padrão (base.html) e a redefinição do bloco conteudo. O código anterior do template ficou dentro do bloco conteudo.

{% extends "base.html" %} {% block conteudo %}

<h1>{{ cliente.nome }}</h1> <ul>

{% for reserva in cliente.reservas_confirmadas %}

<li>Data da reserva: {{ reserva.data_reserva }} -- Data do evento: {{ reserva.data_evento }} -- Quantidade de pessoas: {{ reserva.pessoas }} </li>

{% endfor %} </ul>

<a href="{% url 'reservas:detalhe' cliente.id %}"> Confirmar mais reservas?

</a> {% endblock %}

reservas/templates/reservas/detalhe.html

Da mesma forma o template detalhe.html teve apenas a adição do comando para estender o template base.html e a redefinição do bloco conteudo.

{% extends "base.html" %} {% block conteudo %}

Ca pí tu lo 9 - A rq ui vo s e st át ic os , v ie w s g en ér ic as e t es te s {% if error_message %}

<p><strong>{{ error_message }}</strong></p> {% endif %}

<form action="{% url 'reservas:confirma' cliente.id %}" method="post">

{% csrf_token %}

{% for reserva in cliente.reservas_nao_confirmadas %} <input type="checkbox" name="confirmacao"...

<label for="confirmacao{{ forloop.counter }}">... {% endfor %}

<input type="submit" value="Atualizar" /> </form>

{% endblock %}

Importante perceber que no exemplo anterior parte do código foi omitido por ser repetido. Se esse código for colado na aplicação, não funcionará corretamente.

umas_e_ostras/urls.py

q

1 O projeto precisa de uma view para o novo template “templates/index.html”. 1 A view genérica TemplateView pode ser usada.

A última alteração necessária para customizar a aparência do projeto é a criação da view index do site, que renderizará o template index.html.

Existe uma view genérica básica que pode ser usada para renderizar o template index. É chamada de TemplateView e serve para renderizar templates como esse, que não pre- cisam de um model.

# umas_e_ostras/urls.py

from django.conf.urls import include, url from django.contrib import admin

from django.views.generic import TemplateView urlpatterns = [ url('^$', TemplateView.as_view(template_name='index.html'), name='index'), url(r'^reservas/', include('reservas.urls', namespace='reservas')), url(r'^admin/', admin.site.urls), ]

Basta adicionar a definição da URL para que o template seja renderizado. Não é necessário nem mesmo escrever uma classe e fazer herança para que o funcionamento seja obtido.

url('^$', TemplateView.as_view(template_name='index.html'), name=’index’),

Para ver o site funcionando, basta iniciar o servidor de desenvolvimento e acessar a URL indicada no console: http://localhost:8000/.

Py th on e D ja ng o F un da m en to s

Formulários com django.forms.Form

q

1 Módulo django.forms possui ferramentas para manipular formulários.

1 Contempla a definição dos campos, geração do HTML e captura dos dados recebidos via POST, entre outras coisas.

1 Classe django.forms.Form fornece os mecanismos necessários para o funcionamento. O framework Django também oferece facilidades para criar nossos próprios formulários. No módulo django.forms, estão disponíveis diversas classes e funções para auxiliar a criação de formulários. Vale lembrar que em aplicações do Django o conceito de formulário é um pouco mais amplo. Além do formulário HTML, existe também o objeto form (que gera o formulário HTML ou os dados estruturados retornados quando o formulário é submetido, ou ainda, o conjunto dessas partes funcionando do começo ao fim). Todo esse mecanismo é fornecido pela classe Form do módulo django.forms.

Para explicar seu uso, será criado um formulário de contato. Esse formulário precisará de uma URL para acesso e outra URL para a página de sucesso. Precisará também de uma view para tratar o formulário e outra view para renderizar a página de sucesso. O template templates/base.html precisa ser alterado para adicionar o link de acesso ao formulário de contato. Um template para o formulário de contato e um template para a página de sucesso precisam ser criados. E a classe com o formulário de contato que herda de django.forms. Form precisa ser criada. Vejamos a seguir essas alterações com as respectivas explicações.

Novo arquivo forms.py

Para definir um novo formulário, é necessário implementar uma classe que herde da classe django.forms.Form. Nessa classe são definidos os campos e tipos de cada campo do formulário.

# reservas/forms.py from django import forms class FormContato(forms.Form): email = forms.EmailField() assunto = forms.CharField(label='Assunto', max_length=150) comentarios = forms.CharField(label='Comentários', widget=forms.Textarea)

O exemplo define uma classe para um formulário de contato com os campos: e-mail, assunto e comentários. Os campos aceitam parâmetros que customizam o comportamento do formulário. O parâmetro label define a etiqueta que aparece ao lado do campo no formu- lário. O parâmetro max_length aplica um limite ao tamanho do campo. O parâmetro widget modifica o componente que será usado para coletar a informação no formulário. Nesse exemplo foi usado um componente de texto com várias linhas, o forms.Textarea.

Ca pí tu lo 9 - A rq ui vo s e st át ic os , v ie w s g en ér ic as e t es te s

Template base.html

No template base.html foi adicionado um link no menu de navegação para a URL do formu- lário de contato.

<!DOCTYPE html> <html>

<head>

{% load staticfiles %}

<meta http-equiv="Content-type" content="test/html; charset=UTF-8"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/ jquery.min.js"></script>

<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/ jquery-ui.min.js"></script>

<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}" /> </head> <body> {% block header %} <header></header> {% endblock %} {% block navigation %} <nav>

<a href="{% url 'index' %}">

<div id="inicio" class="navButtons"></div> </a>

<a href="{% url 'reservas:index' %}">

<div id="reservas" class="navButtons"></div> </a>

<div id="cardapio" class="navButtons"></div> <a href="{% url 'reservas:contato' %}">

<div id="contato" class="navButtons"></div> </a> </nav> {% endblock %} <section id="body"> <div class="conteudo"> {% block conteudo %} {% endblock %} </div> </section> </body> </html>

Como os templates usados no projeto estendem esse template base.html, nenhuma outra modificação é necessária.

Py th on e D ja ng o F un da m en to s

Template contato.html

Para apresentar o formulário, utilizamos o template contato.html. Esse template precisa definir uma tag HTML <form> para indicar qual a ação será executada quando o botão Enviar for pressionado.

{% extends "base.html" %} {% block conteudo %}

<form action="{% url 'reservas:contato' %}" method="post" class="elegant-aero"> {% csrf_token %}

{{ form.as_ul }}

<input type="submit" value="Enviar" /> </form>

{% endblock %}

Utilizamos a macro da linguagem de template do Django {% url %} para definir a ação. Além disso é definida uma classe CSS, elegant-aero, para customizar a aparência do formulário. O estilo elegant-aero foi definido no arquivo estático umas_e_ostras/static/css/style.css.

Template obrigado.html

Após o envio do e-mail com sucesso é apresentada uma página de agradecimento. O tem- plate obrigado.html é usado para exibir essa mensagem.

{% extends "base.html" %} {% block conteudo %}

<p>Recebemos sua mensagem. Obrigado pelo contato.</p> <p>Entraremos em contato em breve.</p>

{% endblock %}

Alteração no arquivo urls.py

As URLs de contato e agradecimento precisam ser mapeadas para as views que exibirão o formulário, processar os dados por ele enviados e apresentar a mensagem de agradecimento.

# reservas/urls.py

from django.conf.urls import url from.import views

urlpatterns = [ # ex: /reservas/

url(r'^$', views.index, name='index'), # ex: /reservas/1/ url(r'^(?P<cliente_id>[0-9]+)/$', views.detalhe, name='detalhe'), # ex: /reservas/1/lista/ url(r'^(?P<cliente_id>[0-9]+)/lista/$', views.reservas, name='reservas'), # ex: /reservas/1/confirma/ url(r'^(?P<cliente_id>[0-9]+)/confirma/$', views.confirma, name='confirma'),

Ca pí tu lo 9 - A rq ui vo s e st át ic os , v ie w s g en ér ic as e t es te s # ex: /reservas/contato/ url(r'^contato/$', views.contato, name='contato'), # ex: /reservas/obrigado/ url(r'^obrigado/$', views.obrigado, name='obrigado'), ]

A URL /reservas/contato/ é associada à view views.contato que exibirá o formulário e também receberá os dados para enviar o e-mail. A URL /reservas/obrigado/ é associada à view views.obrigado que exibirá a mensagem de agradecimento.

Alterações no arquivo views.py

Falta então criar as views contato e obrigado, que vão exibir o formulário em branco, pro- cessar os dados, enviar o e-mail e exibir a mensagem de agradecimento.

# reservas/views.py

from django.http import HttpResponse from django.http import HttpResponseRedirect from django.shortcuts import render

from django.shortcuts import get_object_or_404 from django.core.urlresolvers import reverse from .models import Cliente, Reserva from .forms import FormContato def index(request):

ordem = request.GET.get('ordem', 'registrado_em') direcao = request.GET.get('direcao', 'desc')

campos = (field.name for field in Cliente._meta.fields) if ordem not in campos:

ordem = 'registrado_em' if direcao == 'desc': direcao = '-' else:

direcao = ''

ordenacao = '{}{}'.format(direcao, ordem) ultimos_clientes = Cliente.objects.order_by( ordenacao)[:5]

context = {'ultimos_clientes': ultimos_clientes} return render(request, 'reservas/index.html', context) def detalhe(request, cliente_id):

cliente = get_object_or_404(Cliente, pk=cliente_id) return render(request, 'reservas/detalhe.html', {'cliente': cliente})

Py th on e D ja ng o F un da m en to s

def reservas(request, cliente_id):

cliente = get_object_or_404(Cliente, pk=cliente_id) return render(request, 'reservas/lista.html', {'cliente': cliente})

def confirma(request, cliente_id):

cliente = get_object_or_404(Cliente, pk=cliente_id) confirmados = request.POST.getlist('confirmacao') for reserva_id in confirmados:

try:

reserva = cliente.reserva_set.get(pk=reserva_id) except (KeyError, Reserva.DoesNotExist):

# Reapresenta o formulário do cliente.

return render(request, 'reservas/detail.html', { 'cliente': cliente,

'error_message': "Código da reserva não encontrado.", })

else:

reserva.confirmada = True reserva.save()

# Sempre retornar uma HttpResponseRedirect depois de tratar com # sucesso os dados do POST. Isso evita que dados sejam postados # novamente caso o usuário pressione o botão voltar.

return HttpResponseRedirect(reverse('reservas:reservas', args=(cliente.id,))) def contato(request): if request.method == 'POST': form = FormContato(request.POST) if form.is_valid(): assunto = form.cleaned_data['assunto'] comentarios = form.cleaned_data['comentarios'] remetente = form.cleaned_data['email'] destinatarios = ['[email protected]'] try:

send_mail(assunto, comentarios, remetente, destinatarios)

except BadHeaderError:

return HttpResponse('Cabeçalho inválido.') return HttpResponseRedirect('/reservas/obrigado/') else:

form = FormContato()

return render(request, 'reservas/contato.html', {'form': form}) def obrigado(request):

return render(request, ‘reservas/obrigado.html’)

A view contato começa testando o método usado. Quando a URL /reservas/contato é acessada, o método é GET e o resultado do teste condicional if é falso. Portanto, o bloco

Ca pí tu lo 9 - A rq ui vo s e st át ic os , v ie w s g en ér ic as e t es te s else: form = FormContato()

Quando o formulário é preenchido e o botão Enviar for pressionado a view contato é acio- nada novamente. Dessa vez o método é POST, fazendo com que seja criado um objeto Form com os dados do POST. O método is_valid() valida os campos do formulário e os valores que estiverem OK são disponibilizados no atributo cleaned_data. Se todos os campos forem válidos o método retorna True, fazendo com que o e-mail seja enviado através da função send_mail. É importante tratar a exceção BadHeaderError pois existe uma técnica conhecida que utiliza os formulários de e-mail para enviar cabeçalhos de e-mail maliciosos.

if request.method == 'POST': form = FormContato(request.POST) if form.is_valid(): assunto = form.cleaned_data['assunto'] comentarios = form.cleaned_data['comentarios'] remetente = form.cleaned_data['email'] destinatarios = ['[email protected]'] try:

send_mail(assunto, comentarios, remetente, destinatarios) except BadHeaderError:

return HttpResponse('Cabeçalho inválido.') return HttpResponseRedirect(‘/reservas/obrigado/’)

Se tudo der certo, o usuário é redirecionado para a página de agradecimento.

No documento Python e Django Fundamentos (páginas 191-199)