# documentação Django 2.0.8

# Escrevendo sua primeira aplicação Django, parte 4

Este tutorial inicia-se onde o Tutorial 3 terminou. Estamos continuando a aplicação web de enquete e será sobre uma forma simples de processamento de formulário e a redução de nosso código.

## Crie um formulário simples

Vamos atualizar nosso template de detalhamento da enquete (“polls/detail.html”) do último tutorial, para que ele contenha um elemento HTML `<form>`:

polls/templates/polls/detail.html

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

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

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

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 o dado``choice=#`` por POST onde # é o ID da escolha selecionada. Este é o conceito básico sobre formulários HTML.
- Nós definimos o parâmetro `action` do formulário para `{% url 'polls:vote' question.id %}`, e definimos `method=post`. Usando `method=post` (em vez de do `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` passou pelo laço.
- Desde que nós estamos criando um formulário POST (que pode ter o efeito de modificação de dados), nós temos que se preocupar com Cross Site Request Forgeries. Felizmente, você não precisa se preocupar muito, porque o Django vem com um sistema muito fácil de usar para se proteger contra isto. Em suma, todas as formas de POST que são direcionados a URLs internas devem usar uma tag `{% csrf_token%}` de template.

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

polls/urls.py

    path('<int:question_id>/vote/', views.vote, name='vote'),

Nós tambem criamos uma implementação falsa da função `vote()`. Vamos criar a versão real. Adicione o seguinte em `polls/views.py`:

polls/views.py

    from django.http import HttpResponse, HttpResponseRedirect
    from django.shortcuts import get_object_or_404, render
    from django.urls import reverse

    from .models import Choice, Question
    # ...
    def vote(request, question_id):
        question = get_object_or_404(Question, pk=question_id)
        try:
            selected_choice = question.choice_set.get(pk=request.POST['choice'])
        except (KeyError, Choice.DoesNotExist):
            # Redisplay the question voting form.
            return render(request, 'polls/detail.html', {
                'question': question,
                '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('polls:results', args=(question.id,)))

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

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

  Note que Django também fornece `request.GET` para acesar dados GET da mesma forma – mas nós estamos usando attr:request.POST <django.http.HttpRequest.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 `KeyError` caso uma `choice` não seja fornecida via dados POST. O código acima checa por `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 `HttpResponse`. `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 `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 `reverse()` no construtor da `HttpResponseRedirect` neste exemplo. Essa função nos ajuda a evitar de colocar a URL dentro da view de maneira literal. A ele é dado então o nome da “view” que queremos que ele passe o controle e a parte variável do padrão de formato da URL que aponta para a “view”. Neste caso, usando o URLconf nós definimos em Tutorial 3, esta chamada de `reverse()` irá retornar uma string como

      '/polls/3/results/'

  onde o `3` é o valor para `question.id`. Esta URL redirecionada irá então chamar a view `'results'` para exibir a página final.

Como mencionado no Tutorial 3, `request` é um objeto `HttpRequest` object. Para mais informações sobre objetos `HttpRequest`, veja request and response documentation.

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

polls/views.py

    from django.shortcuts import get_object_or_404, render

    def results(request, question_id):
        question = get_object_or_404(Question, pk=question_id)
        return render(request, 'polls/results.html', {'question': question})

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 o template `polls/results.html`:

polls/templates/polls/results.html

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

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

    <a href="{% url 'polls:detail' question.id %}">Vote again?</a>

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.

Nota

O código da nossa view `vote()` tem um pequeno problema. Ele primeiro pega o objeto de `selected_choice` do banco de dados, calcula o novo valor de `votes` e os salva novamente. Se dois usuários do seu site tentarem votar ao mesmo tempo: o mesmo valor, digamos 42, será obtido para `votes`. Então, para ambos usuários, o novo valor 43 é calculado e salvo, quando o valor esperado seria 44.

Isto é chamado de condição de concorrência. Se você estiver interessado, pode ler Avoiding race conditions using F() para aprender como resolver este problema.

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

As views `detail()` (de Tutorial 3) e `results()` são muito simples – e, como mencionado acima, redundantes. A view `index()`, que mostra a lista de votações, é similar.

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. Converta o URLconf.
2. Delete algumas das views antigas, desnecessárias.
3. Introduz novas views baseadas em views genéricas Django’s.

Leia para obter mais detalhes.

Por que o código ficou embarralhado?

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.

### Corrija URLconf

Em primeiro lugar, abra a URLconf `polls/urls.py` e modifique para ficar assim:

polls/urls.py

    from django.urls import path

    from . import views

    app_name = 'polls'
    urlpatterns = [
        path('', views.IndexView.as_view(), name='index'),
        path('<int:pk>/', views.DetailView.as_view(), name='detail'),
        path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
        path('<int:question_id>/vote/', views.vote, name='vote'),
    ]

Note that the name of the matched pattern in the path strings of the second and third patterns has changed from `<question_id>` to `<pk>`.

### Views alteradas

Em seguida, vamos remover a nossas velhas views `index`, `` detail``, e `results` e usar views genéricas do Django em seu lugar. Para fazer isso, abra o arquivo `polls/views.py` e alterar para:

polls/views.py

    from django.http import HttpResponseRedirect
    from django.shortcuts import get_object_or_404, render
    from django.urls import reverse
    from django.views import generic

    from .models import Choice, Question

    class IndexView(generic.ListView):
        template_name = 'polls/index.html'
        context_object_name = 'latest_question_list'

        def get_queryset(self):
            """Return the last five published questions."""
            return Question.objects.order_by('-pub_date')[:5]

    class DetailView(generic.DetailView):
        model = Question
        template_name = 'polls/detail.html'

    class ResultsView(generic.DetailView):
        model = Question
        template_name = 'polls/results.html'

    def vote(request, question_id):
        ... # same as above, no changes needed.

Nós estamos usando duas views genéricas aqui: `ListView` e `DetailView`. 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 é o modelo que ela vai agir. Isto é fornecido usando o atributo `model`.
- A view genérica `DetailView` espera o valor de chave primaria capturado da URL chamada `"pk"`, então mudamos `question_id` para `pk` para as views genérica.

Por padrão, a view genérica `DetailView` utiliza um template chamado `<app name>/<model name>_detail.html`. Em nosso caso, ela vai utilizar o template `"polls/question_detail.html"`. O atributo `template_name` é usado para indicar ao Django para usar um nome de template em vez de auto gerar uma nome de template. Nós também especificamos `template_name` para a view de listagem de `results` – isto garante que a view de detalhe tem uma aparencia quando rederizada, mesmo que sejam tanto um class:~django.views.generic.detail.DetailView por trás das cenas.

Semelhantemente, a view genérica `ListView` utiliza um template chamado `<app name>/<model name>_list.html`; Nós usamos `template_name` para informar `ListView` para usar nossos templates `"polls/index.html"`.

Nas partes anteriores deste tutorial, os templates tem sido fornecidos com um contexto que contém as variáveis `question` e `latest_question_list`. Para a `DetailView` a variavel `question` é fornecida automaticamente – já que estamos usando um model Django (`Question`), Django is able to determine an appropriate name for the context variable. However, for ListView, the automatically generated context variable is `question_list`. To override this we provide the `context_object_name` attribute, specifying that we want to use `latest_question_list` instead. As an alternative approach, you could change your templates to match the new default context variables – but it’s a lot easier to just tell Django to use the variable you want.

Execute o servidor, e use sua nova aplicação de enquete baseada em generic views.

Para detalhes completos sobre views genéricas, veja a documentação de generic views documentation.

Quando você estiver confortável com formulários e generic views, leia a parte 5 deste tutorial para aprender sobre como testar nossa aplicação de enquete.