Escrevendo sua primeira app Django, parte 3¶
Este tutorial inicia-se onde o Tutorial 2 terminou. Vamos continuar a aplicação web de enquete e focaremos na criação da interface pública – “views”.
Onde obter ajuda:
Se tiver problemas enquanto caminha por este tutorial, por favor consulte a seção Obtendo ajuda da FAQ.
Visão Geral¶
Uma view é um “tipo” de página Web em sua aplicação Django que em geral serve a uma função específica e tem um template específico. Por exemplo, em uma aplicação de blog, você deve ter as seguintes views:
- Página inicial do blog - exibe os artigos mais recentes.
- Página de “detalhes” - página de vínculo estático permanente para um único artigo.
- Página de arquivo por ano - exibe todos os meses com artigos para um determinado ano.
- Página de arquivo por mês - exibe todos os dias com artigos para um determinado mês.
- Página de arquivo por dia - exibe todos os artigos de um determinado dia.
- Ação de comentários - controla o envio de comentários para um artigo.
Em nossa aplicação de enquetes, nós teremos as seguintes views:
- Página de “índice” de enquetes - exibe as enquetes mais recente.
- Question “detail” page – displays a question text, with no results but with a form to vote.
- Página de “resultados” de perguntas - exibe os resultados de uma pergunta em particular.
- Ação de voto - gerencia a votação para uma escolha particular em uma enquete em particular.
In Django, web pages and other content are delivered by views. Each view is represented by a Python function (or method, in the case of class-based views). Django will choose a view by examining the URL that’s requested (to be precise, the part of the URL after the domain name).
Now in your time on the web you may have come across such beauties as
ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B
.
You will be pleased to know that Django allows us much more elegant
URL patterns than that.
A URL pattern is the general form of a URL - for example:
/newsarchive/<year>/<month>/
.
To get from a URL to a view, Django uses what are known as ‘URLconfs’. A URLconf maps URL patterns to views.
This tutorial provides basic instruction in the use of URLconfs, and you can refer to Despachante de URL for more information.
Escrevendo mais views¶
Agora vamos adicionar mais algumas views em polls/views.py
. Estas views são um pouco diferentes, porque elas 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)
Wire these new views into the polls.urls
module by adding the following
path()
calls:
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'),
]
Dê uma olhada no seu navegador, em /polls/34/
. Ele vai executar o método detail()
e mostrar o ID que você informou na URL. Tente “/polls/34/results/” e “/polls/34/vote/” também – Eles vão mostrar a pagina resultadoss e a pagina de votação.
Quando alguém requisita uma página do seu site Web – vamos dizer “/polls/34/”, o Django irá carregar o módulo Python mysite.urls
para o qual ele aponta devido a configuração em ROOT_URLCONF
. Ele encontra a variável nominada urlpatterns
e atravessa as descrições na mesma ordem. Depois de encontrar a combinação 'polls/'
, ele reparte o texto encontrado ("polls/"
) e envia o restante – "34/"
– para o URLconf ‘polls.urls’ para processamento posterior. Lá ele encontra '<int:question_id>/'
, resultando em uma chamada para a view detail()
como abaixo:
detail(request=<HttpRequest object>, question_id=34)
The question_id=34
part comes from <int:question_id>
. Using angle
brackets “captures” part of the URL and sends it as a keyword argument to the
view function. The question_id
part of the string defines the name that
will be used to identify the matched pattern, and the int
part is a
converter that determines what patterns should match this part of the URL path.
The colon (:
) separates the converter and pattern name.
Escreva views que façam algo¶
Cada view é responsável por fazer uma das duas coisas: devolver um objeto HttpResponse
contendo o conteúdo para a página requisitada, ou levantar uma exceção como Http404
. O resto é com você.
Sua view pode ler registros do banco de dados, ou não. Ela pode usar um sistema de templates como o do Django - ou outro sistema de templates Python de terceiros - ou não. Ele pode gerar um arquivo PDF, saída em um XML, criar um arquivo ZIP sob demanda, qualquer coisa que você quiser,usando qualquer biblioteca Python você quiser.
Tudo que o Django espera é que a view devolva um HttpResponse
. Ou uma exceção.
Porque é conveniente, vamos usar a própria API de banco de dados do Django, a qual cobrimos em Tutorial 2. Aqui uma nova tentativa de view index()
, a qual mostra as últimas 5 “poll questions” do sistema, separada por vírgulas, de acordo com sua data de publicação:
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)
# Leave the rest of the views (detail, results, vote) unchanged
Há um problema aqui, no entanto: o design da página esta codificado na view. Se você quiser mudar a forma de apresentação de sua página, você terá de editar este código diretamente em Python. Então vamos usar o sistema de templates do Django para separar o design do código Python:
Primeiro, crie um diretório chamado `` templates`` em seu diretório polls
. O Django irá procurar templates lá.
A sua configuração de projeto TEMPLATES
descreve como o Django vai carregar e renderizar templates. O arquivo de configuração padrão usa o backend DjangoTemplates
do qual a opção APP_DIRS
é configurada como True
. Por convenção DjangoTemplates
procura por um subdiretório “templates” em cada uma das INSTALLED_APPS
.
Within the templates
directory you have just created, create another
directory called polls
, and within that create a file called
index.html
. In other words, your template should be at
polls/templates/polls/index.html
. Because of how the app_directories
template loader works as described above, you can refer to this template within
Django as polls/index.html
.
Namespacing de template
Now we might be able to get away with putting our templates directly in
polls/templates
(rather than creating another polls
subdirectory),
but it would actually be a bad idea. Django will choose the first template
it finds whose name matches, and if you had a template with the same name
in a different application, Django would be unable to distinguish between
them. We need to be able to point Django at the right one, and the best
way to ensure this is by namespacing them. That is, by putting those
templates inside another directory named for the application itself.
Ponha o seguinte código neste 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 %}
Nota
To make the tutorial shorter, all template examples use incomplete HTML. In your own projects you should use complete HTML documents.
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 mapeando nomes de variáveis para objetos Python.
Carregue a página apontando seu navegador para “/polls/”, e você deve ver uma lista contendo a questão “What’s up” do Tutorial 2. O link aponta para página de detalhes das perguntas.
Um atalho: render()
¶
É um estilo muito comum carregar um template, preenchê-lo com um contexto e retornar um objeto HttpResponse
com o resultado do template renderizado. O Django fornece este atalho. Aqui esta toda a view index()
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)
Note que uma vez que você tiver feito isto em todas as views, nós não vamos mais precisar importar loader
e HttpResponse
(você vai querer manter HttpResponse
se você ainda tiver os métodos criados para detail
, results
, e vote
).
A função render()
recebe o nome do template como primeiro argumento e um dicionário opcional como segundo argumento. Ele retorna um objeto HttpResponse
do template informado renderizado com o contexto determinado.
Levantando um erro 404¶
Agora, vamos abordar a view de detalhe de pergunta – a página que mostra as questões para uma enquete lançada. Aqui esta 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})
Um novo conceito aqui: A view levanta uma exceção Http404
se a enquete com ID requisitado não existir.
Nós iremos discutir o que você poderia colocar no template polls/detail.html
mais tarde, mas se você gostaria de ter o exemplo acima funcionando rapidamente, um arquivo contendo:
{{ question }}
irá ajudar a começar por enquanto.
Um atalho: get_object_or_404()
¶
É um estilo muito comum usar get()
e levantar uma exceção Http404
se o objeto não existir. O Django fornece um atalho para isso. Aqui esta 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 get_object_or_404()
recebe um modelo do Django como primeiro argumento e uma quantidade arbitrária de argumentos nomeados, que ele passa para a função do módulo get()
. E levanta uma exceção Http404
se o objeto não existir.
Filosofia
Porquê usamos uma função auxiliar get_object_or_404()
ao invés de automaticamente capturar as exceções ObjectDoesNotExist
em alto nível ou fazer a API do modelo levantar Http404
ao invés de ObjectDoesNotExist
?
Porque isso seria acoplar a camada de modelo com a camada de visão. Um dos principais objetivo do design do Django é manter o baixo acoplamento. Alguns acoplamento controlado é introduzido no módulo django.shortcuts
.
Existe também a função get_list_or_404()
, que trabalha da mesma forma que get_object_or_404()
– com a diferença de que ela usa filter()
ao invés de get()
. Ela levanta Http404
se a lista estiver vazia.
Use o sistema de template¶
De volta para a view detail()
da nossa aplicação de enquentes. Dada a variável de contexto question
, aqui está como o template polls/detail.htm
deve 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 uma sintaxe separada por pontos para acessar os atributos da variável. No exemplo de {{ question.question_text }}
, primeiro o Django procura por dicionário no objeto question
. Se isto falhar, ele tenta procurar por um atributo – que funciona, neste caso. Se a procura do atributo também falhar, ele irá tentar uma chamada do tipo list-index.
A chamada do método acontece no laço {% for %}
: poll.choice_set.all
é interpretado como código Python poll.choice_set.all()
, que retorna objetos Choice
iteráveis que são suportado para ser usado na tag {% for %}
.
Veja o guia de templates para maiores detalhes sobre templates.
Removendo URLs codificados nos templates¶
Lembre-se, quando escrevemos o link para uma pergunta no template polls/templates/index.html
, o link foi parcialmente codificado como este:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
O problema com com essa abordagem acoplada, hardcoded é que torna muito difícil alterar URLs em projetos com muitos templates. Todavia, se definimos o argumento name nas funções path()
no módulo polls.urls
, você pode remover a dependência de um caminho de URL específico nas configurações de url usando a template tag {% url %}
:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
A maneira como isso funciona é, observando as definições de URL como especificado no módulo polls.urls
. Você pode ver exatamente onde o nome de URL ‘detalhe’ é definido a seguir
...
# the 'name' value as called by the {% url %} template tag
path('<int:question_id>/', views.detail, name='detail'),
...
Se você quiser alterar a URL das views de detalhe da enquete para outra coisa, talvez para algo como polls/specifics/12/
em vez de fazê-lo no template (ou templates) você mudaria em polls/urls.py
:
...
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...
Namespacing nomes de URL¶
Este projeto tutorial tem apenas um aplicação, polls
. Em projetos reais Django, pode haver cinco, dez, vinte ou mais aplicações. Como o Django diferencia os nomes de URL entre eles? Por exemplo, a aplicação polls
tem uma view detail
, e assim pode um aplicativo no mesmo projeto que é para um blog. Como é que faz para que o Django saiba qual view da aplicação será criada para a url ao usar a tag de template`` {% url%} ``?
A resposta é adicionar namespaces a seu URLconf. No arquivo polls/urls.py
, continue e adicione um app_name
para configurar o namespace da aplicação.
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
Agora mudo 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 d namespace:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
When you’re comfortable with writing views, read part 4 of this tutorial to learn the basics about form processing and generic views.