---
title: "Escrevendo sua primeira aplicação Django, parte 4"
version: 5.0
locale: pt-br
source: https://docs.djangoproject.com/pt-br/5.0/intro/tutorial04/
canonical: https://djangodocs.dev/pt-br/5.0/intro/tutorial04/
---
# Escrevendo sua primeira aplicação Django, parte 4

This tutorial begins where [Tutorial 3](/pt-br/5.0/intro/tutorial03/) left off. We’re
continuing the web-poll application and will focus on form processing and
cutting down our code.

> **Onde obter ajuda:**
>
> Se tiver problemas enquanto caminha por este tutorial, por favor consulte a seção [Obtendo ajuda](/pt-br/5.0/faq/help/) da FAQ.

## 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`*

```html+django
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
    <legend><h1>{{ question.question_text }}</h1></legend>
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    {% 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 %}
</fieldset>
<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.
- We set the form’s `action` to `{% url 'polls:vote' question.id %}`, and we
  set `method="post"`. Using `method="post"` (as opposed to
  `method="get"`) is very important, because the act of submitting this
  form will alter data server-side. Whenever you create a form that alters
  data server-side, use `method="post"`. This tip isn’t specific to
  Django; it’s good web development practice in general.
- `forloop.counter` indica quantas vezes a tag :ttag\`for\` passou pelo laço.
- Since we’re creating a POST form (which can have the effect of modifying
  data), we need to worry about Cross Site Request Forgeries.
  Thankfully, you don’t have to worry too hard, because Django comes with a
  helpful system for protecting against it. In short, all POST forms that are
  targeted at internal URLs should use the [`{% csrf_token %}`](/pt-br/5.0/ref/templates/builtins/#std-templatetag-csrf_token)
  template tag.

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`*

```python
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`*

```python
from django.db.models import F
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 = F("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`](/pt-br/5.0/ref/request-response/#django.http.HttpRequest.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`](/pt-br/5.0/ref/request-response/#django.http.HttpRequest.POST) são sempre strings.

  Note que Django também fornece [`request.GET`](/pt-br/5.0/ref/request-response/#django.http.HttpRequest.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`](https://docs.python.org/3/library/exceptions.html#KeyError) caso uma `choice` não seja fornecida via dados POST. O código acima checa por [`KeyError`](https://docs.python.org/3/library/exceptions.html#KeyError) e re-exibe o formulário da enquete com as mensagens de erro se uma `choice` não for fornecida.
- `F("votes") + 1` [instructs the database](/pt-br/5.0/ref/models/expressions/#avoiding-race-conditions-using-f) to increase the vote count by 1.
- Após incrementar uma opção, o código retorna um class:~django.http.HttpResponseRedirect em vez de um normal       [`HttpResponse`](/pt-br/5.0/ref/request-response/#django.http.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).

  As the Python comment above points out, you should always return an
  [`HttpResponseRedirect`](/pt-br/5.0/ref/request-response/#django.http.HttpResponseRedirect) after successfully dealing with
  POST data. This tip isn’t specific to Django; it’s good web development
  practice in general.
- Estamos usando a função  [`reverse()`](/pt-br/5.0/ref/urlresolvers/#django.urls.reverse) no construtor da  [`HttpResponseRedirect`](/pt-br/5.0/ref/request-response/#django.http.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](/pt-br/5.0/intro/tutorial03/), esta chamada de [`reverse()`](/pt-br/5.0/ref/urlresolvers/#django.urls.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](/pt-br/5.0/intro/tutorial03/), `request` é um objeto [`HttpRequest`](/pt-br/5.0/ref/request-response/#django.http.HttpRequest) object. Para mais informações sobre objetos [`HttpRequest`](/pt-br/5.0/ref/request-response/#django.http.HttpRequest), veja [request and response documentation](/pt-br/5.0/ref/request-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:

*`polls/views.py`*

```python
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`*

```html+django
<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.

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

The `detail()` (from [Tutorial 3](/pt-br/5.0/intro/tutorial03/)) and `results()`
views are very short – and, as mentioned above, redundant. The `index()`
view, which displays a list of polls, is similar.

These views represent a common case of basic web development: getting data from
the database according to a parameter passed in the URL, loading a template and
returning the rendered template. Because this is so common, Django provides a
shortcut, called the “generic views” system.

Generic views abstract common patterns to the point where you don’t even need to
write Python code to write an app. For example, the
[`ListView`](/pt-br/5.0/ref/class-based-views/generic-display/#django.views.generic.list.ListView) and
[`DetailView`](/pt-br/5.0/ref/class-based-views/generic-display/#django.views.generic.detail.DetailView) generic views
abstract the concepts of “display a list of objects” and
“display a detail page for a particular type of object” respectively.

Let’s convert our poll app to use the generic views system, so we can delete a
bunch of our own code. We’ll have to take a few steps to make the conversion.
We will:

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`*

```python
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>`. This is
necessary because we’ll use the
[`DetailView`](/pt-br/5.0/ref/class-based-views/generic-display/#django.views.generic.detail.DetailView) generic view to replace our
`detail()` and `results()` views, and it expects the primary key value
captured from the URL to be called `"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`*

```python
from django.db.models import F
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.
    ...
```

Each generic view needs to know what model it will be acting upon. This is
provided using either the `model` attribute (in this example, `model =
Question` for  `DetailView` and `ResultsView`) or by defining the
[`get_queryset()`](/pt-br/5.0/ref/class-based-views/mixins-multiple-object/#django.views.generic.list.MultipleObjectMixin.get_queryset) method (as
shown in `IndexView`).

Por padrão, a “view” genérica [`DetailView`](/pt-br/5.0/ref/class-based-views/generic-display/#django.views.generic.detail.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 aparência quando renderizada, mesmo que sejam tanto um  class:~django.views.generic.detail.DetailView por trás das cenas.

Semelhantemente, a view genérica [`ListView`](/pt-br/5.0/ref/class-based-views/generic-display/#django.views.generic.list.ListView) utiliza um template chamado `<app name>/<model name>_list.html`; usamos `template_name` para informar [`ListView`](/pt-br/5.0/ref/class-based-views/generic-display/#django.views.generic.list.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 modelo Django (`Question`), Django é capaz de determinar um nome apropriado para a variável de contexto. Contudo, para ListView, a variável de contexto gerada automaticamente é `question_list`. Para sobrescrever nós fornecemos o atributo `context_object_name`, especificando que queremos usar `latest_question_list` no lugar. Como uma abordagem alternativa, você poderia mudar seus templates para casar o novo padrão das variáveis de contexto – mas é muito fácil dizer para o Django usar a variável que você quer.

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](/pt-br/5.0/topics/class-based-views/).

Quando você estiver confortável com formulários e generic views, leia a [parte 5 deste tutorial](/pt-br/5.0/intro/tutorial05/) para aprender sobre como testar nossa aplicação de enquete.
