Introdução
Este tutorial começa onde Configurar o Banco de Dados parou. Estamos continuando a aplicação web de enquetes e focaremos na criação da interface pública -- "views" (visualizações).
Visão Geral
Uma view (visualização) é um "tipo" de página web em sua aplicação Django que geralmente serve a uma função específica e possui um template (modelo) específico. Por exemplo, em uma aplicação de blog, você pode ter as seguintes views:
- Página inicial do blog -- exibe as últimas entradas.
- Página de "detalhes" da entrada -- página de permalink (link permanente) para uma única entrada.
- Página de arquivo baseada em ano -- exibe todos os meses com entradas no ano especificado.
- Página de arquivo baseada em mês -- exibe todos os dias com entradas no mês especificado.
- Página de arquivo baseada em dia -- exibe todas as entradas no dia especificado.
- Ação de comentário -- lida com a publicação de comentários em uma determinada entrada.
Em nossa aplicação de enquetes, teremos as seguintes quatro views:
- Página "index" (índice) da pergunta -- exibe as últimas perguntas.
- Página de "detalhes" da pergunta -- exibe o texto da pergunta, sem resultados, mas com um formulário para votar.
- Página de "resultados" da pergunta -- exibe os resultados de uma pergunta específica.
- Ação de voto -- lida com a votação em uma escolha específica em uma pergunta específica.
No Django, páginas web e outros conteúdos são entregues por views. Cada view é representada por uma função Python (ou método, no caso de views baseadas em classe). O Django escolherá uma view examinando a URL que foi solicitada (para ser preciso, a parte da URL após o nome do domínio).
Agora, em seu tempo na web, você pode ter se deparado com belezas como ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B. Você ficará satisfeito em saber que o Django nos permite padrões de URL muito mais elegantes do que isso.
Um padrão de URL é a forma geral de uma URL - por exemplo: /newsarchive/<ano>/<mês>/.
Para ir de uma URL para uma view, o Django usa o que é conhecido como 'URLconfs'. Uma URLconf mapeia padrões de URL para views.
Este tutorial fornece instruções básicas sobre o uso de URLconfs, e você pode consultar /topics/http/urls para obter mais informações.
Escrevendo mais views
Agora, vamos adicionar mais algumas views a polls/views.py. Essas views são ligeiramente diferentes, porque recebem um argumento:
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
Conecte essas novas views ao módulo polls.urls adicionando as seguintes chamadas ~django.urls.path:
Edite o arquivo polls/urls.py e adicione as seguintes linhas:
from django.urls import path
from . import views
urlpatterns = [
## ex: /polls/
path("", views.index, name="index"),
## ex: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
## ex: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
## ex: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]
Agora, execute o servidor novamente:
cd ~/project/mysite
python manage.py runserver 0.0.0.0:8080
Mude para a aba Web 8080, em /polls/34/. Ele executará o método detail() e exibirá qualquer ID que você fornecer na URL. Tente /polls/34/results/ e /polls/34/vote/ também -- estes exibirão os resultados e as páginas de votação de espaço reservado.

Quando alguém solicita uma página do seu site -- por exemplo, /polls/34/, o Django carregará o módulo Python mysite.urls porque ele é apontado pela configuração ROOT_URLCONF. Ele encontra a variável chamada urlpatterns e percorre os padrões em ordem. Depois de encontrar a correspondência em 'polls/', ele remove o texto correspondente ("polls/") e envia o texto restante -- "34/" -- para a URLconf 'polls.urls' para processamento adicional. Lá, ele corresponde a '<int:question_id>/', resultando em uma chamada para a view detail() da seguinte forma:
detail(request=<HttpRequest object>, question_id=34)
A parte question_id=34 vem de <int:question_id>. O uso de colchetes angulares "captura" parte da URL e a envia como um argumento de palavra-chave para a função de view. A parte question_id da string define o nome que será usado para identificar o padrão correspondente, e a parte int é um conversor que determina quais padrões devem corresponder a esta parte do caminho da URL. Os dois pontos (:) separam o conversor e o nome do padrão.
Escreva views que realmente façam algo
Cada view é responsável por fazer uma de duas coisas: retornar um objeto ~django.http.HttpResponse contendo o conteúdo para a página solicitada, ou lançar uma exceção como ~django.http.Http404. O resto depende de você.
Sua view pode ler registros de um banco de dados, ou não. Ela pode usar um sistema de template como o do Django -- ou um sistema de template Python de terceiros -- ou não. Ela pode gerar um arquivo PDF, gerar XML, criar um arquivo ZIP na hora, qualquer coisa que você quiser, usando as bibliotecas Python que você quiser.
Tudo o que o Django quer é um ~django.http.HttpResponse. Ou uma exceção.
Como é conveniente, vamos usar a própria API de banco de dados do Django, que cobrimos no Tutorial 2. Aqui está uma tentativa de uma nova view index(), que exibe as últimas 5 perguntas da enquete no sistema, separadas por vírgulas, de acordo com a data de publicação:
Edite o arquivo polls/views.py e altere-o para que se pareça com isto:
from django.http import HttpResponse
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
output = ", ".join([q.question_text for q in latest_question_list])
return HttpResponse(output)
## Deixe o resto das views (detail, results, vote) inalterado
Há um problema aqui, no entanto: o design da página é codificado na view. Se você quiser mudar a aparência da página, terá que editar este código Python. Então, vamos usar o sistema de template do Django para separar o design do Python, criando um template que a view possa usar.
Primeiro, crie um diretório chamado templates em seu diretório polls. O Django procurará templates lá.
A configuração TEMPLATES do seu projeto descreve como o Django carregará e renderizará os templates. O arquivo de configurações padrão configura um backend DjangoTemplates cuja opção APP_DIRS <TEMPLATES-APP_DIRS> está definida como True. Por convenção, DjangoTemplates procura um subdiretório "templates" em cada um dos INSTALLED_APPS.
Dentro do diretório templates que você acabou de criar, crie outro diretório chamado polls e, dentro dele, crie um arquivo chamado index.html. Em outras palavras, seu template deve estar em polls/templates/polls/index.html. Por causa de como o carregador de template app_directories funciona, conforme descrito acima, você pode se referir a este template dentro do Django como polls/index.html.
Namespacing de template
Agora, poderíamos nos safar colocando nossos templates diretamente em polls/templates (em vez de criar outro subdiretório polls), mas na verdade seria uma má ideia. O Django escolherá o primeiro template que encontrar cujo nome corresponda, e se você tivesse um template com o mesmo nome em uma aplicação diferente, o Django não conseguiria distinguir entre eles.
Precisamos ser capazes de apontar o Django para o correto, e a melhor maneira de garantir isso é namespacing (definir namespace) eles. Ou seja, colocando esses templates dentro de outro diretório nomeado para a própria aplicação.
Coloque o seguinte código nesse template:
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
Observação:
Para tornar o tutorial mais curto, todos os exemplos de template usam HTML incompleto. Em seus próprios projetos, você deve usar documentos HTML completos.
Agora, vamos atualizar nossa view index em polls/views.py para usar o template:
from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
template = loader.get_template("polls/index.html")
context = {
"latest_question_list": latest_question_list,
}
return HttpResponse(template.render(context, request))
Esse código carrega o template chamado polls/index.html e passa um contexto para ele. O contexto é um dicionário que mapeia nomes de variáveis de template para objetos Python.
Execute o servidor novamente:
python manage.py runserver 0.0.0.0:8080
Carregue a página apontando seu navegador para "/polls/", e você deverá ver uma lista com marcadores contendo a pergunta "What's up" do Tutorial 2. O link aponta para a página de detalhes da pergunta.

Um atalho: ~django.shortcuts.render
É um idioma muito comum carregar um template, preencher um contexto e retornar um objeto ~django.http.HttpResponse com o resultado do template renderizado. O Django fornece um atalho. Aqui está a view index() completa, reescrita:
from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
context = {"latest_question_list": latest_question_list}
return render(request, "polls/index.html", context)
Observe que, uma vez que fizemos isso em todas essas views, não precisamos mais importar ~django.template.loader e ~django.http.HttpResponse (você vai querer manter HttpResponse se ainda tiver os métodos stub para detail, results e vote).
A função ~django.shortcuts.render recebe o objeto de solicitação como seu primeiro argumento, um nome de template como seu segundo argumento e um dicionário como seu terceiro argumento opcional. Ele retorna um objeto ~django.http.HttpResponse do template fornecido renderizado com o contexto fornecido.
Levantando um erro 404
Agora, vamos abordar a view de detalhes da pergunta -- a página que exibe o texto da pergunta para uma determinada enquete. Aqui está a view:
from django.http import Http404
from django.shortcuts import render
from .models import Question
## ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, "polls/detail.html", {"question": question})
O novo conceito aqui: A view levanta a exceção ~django.http.Http404 se uma pergunta com o ID solicitado não existir.
Discutiremos o que você pode colocar naquele template polls/detail.html um pouco mais tarde, mas se você quiser fazer o exemplo acima funcionar rapidamente, um arquivo contendo apenas:
{{ question }}
irá te iniciar por agora.
Um atalho: ~django.shortcuts.get_object_or_404
É um idioma muito comum usar ~django.db.models.query.QuerySet.get e levantar ~django.http.Http404 se o objeto não existir. O Django fornece um atalho. Aqui está a view detail(), reescrita:
from django.shortcuts import get_object_or_404, render
from .models import Question
## ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, "polls/detail.html", {"question": question})
A função ~django.shortcuts.get_object_or_404 recebe um modelo Django como seu primeiro argumento e um número arbitrário de argumentos de palavra-chave, que ele passa para a função ~django.db.models.query.QuerySet.get do gerenciador do modelo. Ele levanta ~django.http.Http404 se o objeto não existir.
Por que usamos uma função auxiliar ~django.shortcuts.get_object_or_404 em vez de capturar automaticamente as exceções ~django.core.exceptions.ObjectDoesNotExist em um nível superior, ou fazer com que a API do modelo levante ~django.http.Http404 em vez de ~django.core.exceptions.ObjectDoesNotExist?
Porque isso acoplaria a camada do modelo à camada da view. Um dos principais objetivos de design do Django é manter o acoplamento frouxo. Algum acoplamento controlado é introduzido no módulo django.shortcuts.
Há também uma função ~django.shortcuts.get_list_or_404, que funciona como ~django.shortcuts.get_object_or_404 -- exceto usando ~django.db.models.query.QuerySet.filter em vez de ~django.db.models.query.QuerySet.get. Ele levanta ~django.http.Http404 se a lista estiver vazia.
Use o sistema de templates
Voltando à view detail() para nossa aplicação de enquetes. Dada a variável de contexto question, aqui está como o template polls/detail.html pode ser:
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
O sistema de templates usa a sintaxe de busca por ponto para acessar atributos de variáveis. No exemplo de {{ question.question_text }}, primeiro o Django faz uma busca no dicionário no objeto question. Falhando nisso, ele tenta uma busca de atributo -- que funciona, neste caso. Se a busca de atributo tivesse falhado, ele teria tentado uma busca de índice de lista.
A chamada de método acontece no loop {% for %}<for>: question.choice_set.all é interpretado como o código Python question.choice_set.all(), que retorna um iterável de objetos Choice e é adequado para uso na tag {% for %}<for>.
Removendo URLs hardcoded em templates
Lembre-se que, quando escrevemos o link para uma pergunta no template polls/index.html, o link foi parcialmente hardcoded (codificado diretamente) assim:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
O problema com essa abordagem hardcoded, fortemente acoplada, é que se torna desafiador alterar URLs em projetos com muitos templates. No entanto, como você definiu o argumento name nas funções ~django.urls.path no módulo polls.urls, você pode remover a dependência de caminhos de URL específicos definidos em suas configurações de URL usando a tag de template {% url %}:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
A forma como isso funciona é procurando a definição da URL conforme especificado no módulo polls.urls. Você pode ver exatamente onde o nome da URL 'detail' é definido abaixo:
## o valor 'name' como chamado pela tag de template {% url %}
path("<int:question_id>/", views.detail, name="detail"),
Se você quiser alterar a URL da view de detalhes das enquetes para outra coisa, talvez para algo como polls/specifics/12/, em vez de fazê-lo no template (ou templates), você a alteraria em polls/urls.py:
Você não precisa alterar o template em absoluto.
## adicionou a palavra 'specifics'
path("specifics/<int:question_id>/", views.detail, name="detail"),
Namespacing (Espaço de Nomes) de Nomes de URL
O projeto do tutorial tem apenas um app, polls. Em projetos Django reais, pode haver cinco, dez, vinte apps ou mais. Como o Django diferencia os nomes de URL entre eles? Por exemplo, o app polls tem uma view detail, e o mesmo pode acontecer com um app no mesmo projeto que é para um blog. Como se faz para que o Django saiba qual view de app criar para uma URL ao usar a tag de template {% url %}?
A resposta é adicionar namespaces (espaços de nomes) ao seu URLconf. No arquivo polls/urls.py, adicione um app_name para definir o namespace da aplicação:
from django.urls import path
from . import views
app_name = "polls"
urlpatterns = [
## ex: /polls/
path("", views.index, name="index"),
## ex: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
## ex: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
## ex: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]
Agora, altere seu template polls/index.html de:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
para apontar para a view de detalhes com namespace:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

Quando você estiver confortável com a escrita de views, leia Processamento de Formulários e Reduzindo Nosso Código para aprender o básico sobre processamento de formulários e views genéricas.
Resumo
Parabéns! Você concluiu o laboratório Criando as Views da Interface Pública. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.