Django Brasil


Documentação em português


Escrevendo sua primeira aplicação Django, parte 4

Este tutorial inicia-se onde o Tutorial 3 terminou. Estamos continuando a aplicação Web-Enquete que vai focar em uma forma simples de transformação e de redução de nosso código.

Escrever um formulário simples

Vamos atualizar o template de detalhe ("polls/detail.html") do último tutorial, para que ele contenha um elemento HTML <Form>:

<h1>{{ poll.question }}</h1>

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

<form action="/polls/{{ poll.id }}/vote/" method="post">
{% for choice in poll.choice_set.all %}
    <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
    <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>

Uma rápida explicação:

  • O template acima mostra um botão radio para cada opção da enquete. O valor (value) de cada botão radio está associado ao ID da opção. O nome (name) de cada botão radio é a escolha (choice). Isso significa que, quando alguém escolhe um dos botões de radio e submete a formulário, ele vai enviar choice=3 por POST. Este são os formulários HTML 101.
  • Nós definimos o parametro action do formulário para /polls/{{ poll.id }}/vote/, e method para post. Usar método post (em vez do método get) é muito importante, porque o ato de enviar este formulário irá alterar dados do lado servidor. Sempre que criar um formulário que modifiquem os dados do lado do servidor, utilize method="post". Esta dica não é específica do Django; mais sim uma boa prática para desenvolvimento é Web.
  • forloop.counter indica quantas vezes a tag``for`` atravessou o seu ciclo. Para mais informações, consulte a documentação da tag "for".

Agora, vamos criar uma view que se manipula os dados enviados e faz algo com eles. Lembre-se, no Tutorial 3, criámos uma URLconf para a aplicação de enquete que inclui esta linha:

(r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),

Portanto, vamos criar uma função vote(), em mysite/polls/views.py:

from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from mysite.polls.models import Choice, Poll
# ...
def vote(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    try:
        selected_choice = p.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the poll voting form.
        return render_to_response('polls/detail.html', {
            'poll': p,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse('mysite.polls.views.results', args=(p.id,)))

Este código inclui algumas coisas que ainda não foram abrangidos, neste tutorial:

  • request.POST é um objeto como dicionários que lhe permite acessar os dados submetidos pelas suas chaves. Neste caso, request.POST['choice'] retorna o ID da opção selecionada, tal como uma string. Os valores de request.POST são sempre strings.

    Note que Django também prove request.GET para acesar dados GET da mesma forma -- mas nós estamos usando request.POST explicitamente no nosso código, a fim de garantir que os dados só podem ser alterados por meio de uma chamada POST.

  • request.POST['choice'] irá levantar a exceção KeyError caso uma opção (choice) não seja fornecida via POST. O código acima checa por KeyError e mostra o formulário da enquete com as mensagens de erro se uma opção choice não é dado fornecida.

  • Após incrementar uma opção, o código retorna um HttpResponseRedirect em vez de um HttpResponse. HttpResponseRedirect recebe um único argumento: a URL para o qual o usuário será redirecionado (ver o ponto seguinte para saber como construir a URL, neste caso).

    Como o comentário acima salienta, você deve sempre retornar uma HttpResponseRedirect depois de lidar com sucesso com dados POST. Esta dica não é específica do Django; mais sim uma boa prática para desenvolvimento é Web.

  • Estamos usando a função reverse() no contrução do HttpResponseRedirect neste exemplo. Essa função ajuda a evitar ter que escrever na mão a URL na função da view. É dado o nome da view de que queremos passar o controle e uma porção da variável do padrão de URL que aponta para essa view. Neste caso, usando o URLConf criado no Tutorial 3, esta chamada a reverse() irá retornar uma string como:

    '/polls/3/results/'
    

    ... Onde o 3 é o valor de p.id. Esta URL redirecionada então chama 'results' afim de exibir a página final. Note que você precisará usar o nome completo da view aqui (incluindo o prefixo).

    Para obter mais informações sobre reverse(), veja a documentação do URL dispatcher.

Tal como mencionado no Tutorial 3, request é um objeto HTTPRequest. Para mais informações sobre o objeto HTTPRequest, consulte request and response documentation.

Depois que alguém votar em uma enquete, a vote() view redireciona para a página de resultados da enquete. Vamos escrever essa view:

def results(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('polls/results.html', {'poll': p})

Isto é quase exatamente o mesmo que a view detail() do Tutorial 3.. A única diferença é o nome do template. Iremos corrigir esta redundância depois.

Agora, crie um template results.html:

<h1>{{ poll.question }}</h1>

<ul>
{% for choice in poll.choice_set.all %}
    <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

Agora, vá para /polls/1/ no seu navegador e vote em uma enquete. Você deverá ver uma página de resultados que será atualizado cada vez que você votar. Se você enviar o formulário sem ter escolhido uma opção, você deverá ver a mensagem de erro.

Use views genéricas: Menos código é melhor

A view, detail() (do Tutorial 3) e results() são estupidamente simples -- e, como já mencionado, são redundante. A view, index() (também do Tutorial 3), que exibe uma lista de enquetes, é semelhante.

Estas views representam um caso basico comum de Desenvolvimento Web: obtendo dados do banco de dados de acordo com um parâmetro passado na URL, carregando um template e devolvenfo um template renderizado. Porque isto é tão comum, Django fornece um atalho, chamado sistema de ?views genéricas?.

Views genericas abstraem padrões comuns para um ponto onde você nem precisa de escrever código Python para escrever um aplicação.

Vamos converter a nossa aplicação de enquete para utilizar o sistema de views genéricas, por isso podemos excluir um monte de nosso próprio código. Iremos apenas ter que executar alguns passos para fazer a conversão.

Por que o código se arrastou?

Geralmente, quando escrever uma aplicação Django, você vai avaliar se views genéricas são uma escolha adequada para o seu problema e você irá utilizá-los desde o início em vez de refatorar seu código meio do caminho. Mas este tutorial intencionalmente tem focado em escrever views genéricas "do jeito mais dificil" até agora, para se concentrar nos conceitos fundamentais.

Você deve saber matemática básica antes de você começar a usar uma calculadora.

Em primeiro lugar, abra polls/urls.py URLconf. Ele se parece assim, de acordo com o tutorial até o momento:

from django.conf.urls.defaults import *

urlpatterns = patterns('mysite.polls.views',
    (r'^$', 'index'),
    (r'^(?P<poll_id>\d+)/$', 'detail'),
    (r'^(?P<poll_id>\d+)/results/$', 'results'),
    (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
)

Altere para ficar assim:

from django.conf.urls.defaults import *
from mysite.polls.models import Poll

info_dict = {
    'queryset': Poll.objects.all(),
}

urlpatterns = patterns('',
    (r'^$', 'django.views.generic.list_detail.object_list', info_dict),
    (r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
    url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'),
    (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
)

Nós estamos usando duas views genéricas aqui: object_list e object_detail.

Respectivamente, essas duas views abstraem o conceito de ?exibir uma lista de objetos? e ?exibir uma página de detalhe para um tipo particular de objeto.?

  • Cada view genérica precisa saber que tipo de dado ela vai agir em cima. Esses dados são fornecidos em um dicionário. A chave queryset neste dicionário aponta para a lista de objetos a serem manipulados pela view genérica.
  • A view genérica object_detail espera o valor ID capturado da URL chamado de "object_id", de modo que alteramos poll_id para object_id para view genérica.
  • Nós adicionamos o nome, poll_results, para os resultados da view assim nós temos uma maneira de se referir à sua URL depois (veja a documentação sobre naming URL patterns para maiores informação). Também estamos usando a função url() de django.conf.urls.defaults aqui. É um bom hábito de usar url() quando você estiver fornecendo um padrão de nome como este.

Por padrão, a view genérica object_detail utiliza um template chamado <app name>/<model name>_detail.html. No nosso caso, ela vai utilizar o template "polls/poll_detail.html". Assim, renomeie o seu template polls/detail.html para polls/poll_detail.html e e altere render_to_response() na linha vote().

Do mesmo modo, a view genérica utiliza um template chamado <app name>/<model name>_list.html. Assim, renomeie polls/index.html para polls/poll_list.html.

Porque nós temos mais de uma entrada no URLconf que usa object_detail para a aplicação de enquete, nós especificamos manualmente um nome para o template de resultados: template_name='polls/results.html'. Caso contrário, as duas views utilizariam o mesmo template. Note que nós usamos dict() para retornar um dicionário alterado no lugar.

Note

all() é lazy

Poderá parecer um pouco assustador ver Poll.objects.all() ser utilizado na view de detalhe que necessita apenas de um objeto Poll mas não se preocupe; Poll.objects.all() é na verdade um objeto especial chamado de QuerySet, que é "lazy" e não toca no seu banco de dados a menos que seja absolutamente nescesario.Até o momento consulta ao banco de dados acontecer, a view genérica object_detail terá limitado o seu escopo para um único objeto, de modo que a eventual consulta só irá selecionar uma linha de base de dados.

Se você quiser saber mais sobre seu funcionamento, A documentação de banco de dados do Django explica a natureza lazy dos abjetos QuerySet.

Nas partes anteriores deste tutorial, os templates tem sido fornecidos com um context que contém as variáveis de contexto poll` e latest_poll_list. No entanto, as views fornecem as variáveis object e object_list como contexto. Portanto, você precisa mudar seus templates para combinar com as novas variáveis de contexto. Vá através de seus templates, e alterar qualquer referência a latest_poll_list para object_list e alterar qualquer referência de poll para object.

Agora você pode apagar as views, index(), detail() and results() do arquivo polls/views.py. Não precisamos delas mais - eles foram substituídos por views genéricas.

A view vote() ainda é necessária. No entanto, ela deve ser modificado para corresponder ao novo contexto de variáveis. Na chamada render_to_response(), mude o nome variável de contexto de poll para object.

A última coisa a fazer é corrigir o manioulador da URL para levar em conta a utilização de views genéricas. Na view vote() anterior, usamos a função reverse() para evitar escrever na mão nossa URLs. Agora que mudamos para view genérica, nós vamos precisar alterar a chamada reverse() para apontar de volta para a nossa nova view genérica. Nós não podemos simplesmente utilizar a função view mais - views genéricas podem ser (e são) utilizada várias vezes - mas podemos usar o nome que foi dado:

return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))

Execute o servidor e use o sua nova aplicação de enquete baseada views genéricas.

Para maiores detalhes sobre views genéricass, consulte a generic views documentation.

Em breve

O tutorial acaba aqui por enquanto. Mas vá logo para as próximas partes:

  • Processamento avancado de formulário
  • Usando o framework de RSS
  • Usando o framework de cache
  • Usando o framework de comentários
  • Características avançãdas da interface de administração: Permissões
  • Características avançãdas da interface de administração: Customização de JavaScript

Entretanto, você pode ler o restante da documentação Django e começar a escrever suas próprias aplicações.


Hospedado por PyTown.com. Django Brasil é a comunidade brasileira de usuários do framework web Django. Django é uma marca registrada de Lawrence Journal-World.