.. .. META INFORMATION OF TRANSLATION .. .. $TranslationStatus: Done $ .. $OriginalRevision: 11348 $ .. $TranslationAuthors: Wiliam Alves de Souza $ .. .. INFO OF THIS FILE (DO NOT EDIT! UPDATED BY SUBVERSION) .. .. $HeadURL$ .. $LastChangedRevision$ .. $LastChangedBy$ .. $LastChangedDate$ .. .. _intro-tutorial04: ================================================= Escrevendo sua primeira aplicação Django, parte 4 ================================================= Este tutorial inicia-se onde o :ref:`Tutorial 3 ` terminou. Estamos continuando a aplicação web de enquete e concentraremos em uma forma simples de processamento de formulário e a redução de nosso código. Escreva um simples formulário ============================= Vamos atualizar nosso template de detalhamento da enquete ("polls/detail.html") do último tutorial, para que ele contenha um elemento HTML ``
``: .. code-block:: html+django

{{ poll.question }}

{% if error_message %}

{{ error_message }}

{% endif %} {% for choice in poll.choice_set.all %}
{% endfor %}
Uma rápida explicação: * O template acima exibe um botão radio para cada opção da enquete. O ``value`` de cada botão radio está associado ao ID da opção. O ``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 parâmetro ``action`` do formulário para /polls/{{ poll.id }}/vote/, e definimos ``method=post``. Usando ``method=post`` (em vez de ``method=get``) é muito importante, porque o ato de enviar este formulário irá alterar dados do lado servidor. Sempre que criar um formulário que modifique os dados do lado do servidor, utilize ``method="post"``. Esta dica não é específica do Django; mais sim uma boa prática para o desenvolvimento é Web. * ``forloop.counter`` indica quantas vezes a tag :ttag`for` atravessou o seu ciclo. Agora, vamos criar uma ``view`` Django que manipula os dados submetidos e faz algo com eles. Lembre-se, no :ref:`Tutorial 3 `, criamos uma URLconf para a aplicação de enquete que inclui esta linha:: (r'^(?P\d+)/vote/$', 'vote'), Então, vamos criar um 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 cobertas neste tutorial: * :attr:`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 :attr:`request.POST ` são sempre strings. Note que Django também fornece :attr:`request.GET ` para acesar dados GET da mesma forma -- mas nós estamos usando :attr:`request.POST ` explicitamente no nosso código, para garantir que os dados só podem ser alterados por meio de uma chamada POST. * ``request.POST['choice']`` irá levantar a exceção :exc:`KeyError` caso uma ``choice`` não seja fornecida via POST. O código acima checa por :exc:`KeyError` e re-exibe o formulário da enquete com as mensagens de erro se uma ``choice`` não for fornecida. * Após incrementar uma opção, o código retorna um :class:`~django.http.HttpResponseRedirect` em vez de um normal :class:`~django.http.HttpResponse`. :class:`~django.http.HttpResponseRedirect`` recebe um único argumento: a URL para o qual o usuário será redirecionado (veja o ponto seguinte para saber como construímos a URL, neste caso). Como o comentário Python acima salienta, você deve sempre retornar uma :class:`~django.http.HttpResponseRedirect` depois de lidar com sucesso com dados POST. Esta dica não é específica do Django; mais sim uma boa prática para o desenvolvimento Web. * Estamos usando a função :func:`~django.core.urlresolvers.reverse` no construtor do :class:`~django.http.HttpResponseRedirect` neste exemplo. Essa função ajuda a evitar ter que escrever na mão a URL na função de 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 :func:`~django.core.urlresolvers.reverse` irá retornar uma string como:: '/polls/3/results/' ... onde o ``3`` é o valor de ``p.id``. Esta URL redirecionada irá então chamar a view ``'results'`` afim de exibir a página final. Note que você precisará usar o nome completo da ``view`` aqui (incluindo o prefixo). Como mencionado no Tutorial 3, ``request`` é um objeto :class:`~django.http.HttpRequest`. Para mais informações sobre o objeto :class:`~django.http.HttpRequest`, veja a :ref:`documentação do request e response `. Depois que alguém votar em uma enquete, a view ``vote()`` 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 :ref:`Tutorial 3 `. A única diferença é o nome do template. Iremos corrigir esta redundância depois. Agora, crie um template ``results.html``: .. code-block:: html+django

{{ poll.question }}

    {% for choice in poll.choice_set.all %}
  • {{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}
  • {% endfor %}
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 ========================================== As views ``detail()`` (do :ref:`Tutorial 3 `) e ``results()`` são estupidamente simples -- e, como já mencionado acima, são redundantes. A view, ``index()`` (também do Tutorial 3), que exibe uma lista de enquetes, é semelhante. Estas views representam um caso comum do desenvolvimento Web básico: obter dados do banco de dados de acordo com um parâmetro passado na URL, carregar um template e devolvê-lo renderizado. Por isto ser muito comum, o Django fornece um atalho, chamado sistema de "views genéricas". Views genéricas abstraem padrões comuns para um ponto onde você nem precisa escrever código Python para escrever uma aplicação. Vamos converter a nossa aplicação de enquete para utilizar o sistema de views genéricas, por isso podemos excluir um monte do nosso próprio código. Iremos apenas ter que executar alguns passos para fazer a conversão. Nós iremos: 1. Converter a URLconf. 2. Renomear alguns templates. 3. Remover algumas views antigas e desnecessárias. 4. Corrigir a manipulação de URL para as novas views. Leia a respeito para obter mais detalhes. .. admonition:: Por que o código se arrastou? Geralmente, quando estiver escrevendo uma aplicação Django, você vai avaliar se views genéricas são uma escolha adequada para o seu problema e você irá utilizá-las desde o início em vez de refatorar seu código no meio do caminho. Mas este tutorial intencionalmente tem focado em escrever views "do jeito mais difícil" até agora, para concentrarmos nos conceitos fundamentais. Você deve saber matemática básica antes de você começar a usar uma calculadora. Em primeiro lugar, abra a URLconf ``polls/urls.py``. Ela esta assim, de acordo com o tutorial até o momento:: from django.conf.urls.defaults import * urlpatterns = patterns('mysite.polls.views', (r'^$', 'index'), (r'^(?P\d+)/$', 'detail'), (r'^(?P\d+)/results/$', 'results'), (r'^(?P\d+)/vote/$', 'vote'), ) Modifique 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\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict), url(r'^(?P\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'), (r'^(?P\d+)/vote/$', 'mysite.polls.views.vote'), ) Nós estamos usando duas views genéricas aqui: :func:`~django.views.generic.list_detail.object_list` e :func:`~django.views.generic.list_detail.object_list`. 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 qual 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 :func:`~django.views.generic.list_detail.object_detail` espera o valor ID capturado da URL em ``"object_id"``, de modo que alteramos ``poll_id`` para ``object_id`` para as views genérica. * Nós adicionamos um 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 :ref:`naming URL patterns ` para informações). Também estamos usando a função :func:`~django.conf.urls.default.url` de :mod:`django.conf.urls.defaults` aqui. É um bom hábito usar :func:`~django.conf.urls.defaults.url` quando você estiver fornecendo um padrão de nome como este. Por padrão, a view genérica :func:`~django.views.generic.list_detail.object_detail` utiliza um template chamado ``/_detail.html``. Em 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 altere a linha :func:`~django.shortcuts.render_to_response em ``vote()``. Semelhantemente, a view genérica :func:`~django.views.generic.list_detail.object_list` utiliza um template chamado ``/_list.html``. Assim, renomeie ``polls/index.html`` para ``polls/poll_list.html``. Por termos mais de uma entrada na URLconf que usa :func:`~django.views.generic.list_detail.object_detail` para a aplicação de enquete, nós especificamos manualmente um nome para a view 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:: :meth:`django.db.models.QuerySet.all` é preguiçosa 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 :class:`~django.db.models.QuerySet`, que é "preguiçosa" e não toca no seu banco de dados a menos que seja absolutamente necessário. No momento em que a consulta ao banco de dados acontecer, a view genérica :func:`~django.views.generic.list_detail.object_detail` terá limitado o seu escopo para um único objeto, de modo que a eventual consulta só irá selecionar uma linha da base de dados. Se você quiser saber mais sobre seu funcionamento, a documentação de banco de dados do Django :ref:`explica a natureza preguiçosa 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 genérica 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 altere qualquer referência a ``latest_poll_list`` para ``object_list`` e altere qualquer referência de ``poll`` para ``object``. Agora você pode deletar as views, ``index()``, ``detail()`` e ``results()`` do arquivo ``polls/views.py``. Não precisamos delas mais -- elas foram substituídas por views genéricas. A view ``vote()`` ainda é necessária. No entanto, ela deve ser modificada para corresponder ao novo contexto de variáveis. Na chamada :func:`~django.shortcuts.render_to_response`, renomeie a variável de contexto de ``poll`` para ``object``. A última coisa a fazer é corrigir o manipulador da URL para levar em conta a utilização de views genéricas. Na view ``vote()`` acima, usamos a função :func:`~django.core.urlresolvers.reverse` para evitar escrever na mão nossas URLs. Agora que mudamos para view genérica, nós vamos precisar alterar a chamada :func:`~django.core.urlresolvers.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 sua nova aplicação de enquete baseada em views genéricas. Para maiores detalhes sobre views genéricas, consulte a :ref:`documentação sobre views genéricas `. Brevemente ========== O tutorial acaba aqui por enquanto. Futuras partes deste tutorial cobrirão: * Processamento avançado de formulário * Usando o framework de RSS * Usando o framework de cache * Usando o framework de comentários * Características avançadas da interface de administração: Permissões * Características avançadas da interface de administração: JavaScript personalizado Por enquanto, você pode querer verificar alguns pontos em :ref:`para onde ir a partir daqui `