Escrevendo sua primeira app Django, parte 3

This tutorial begins where Tutorial 2 left off. We’re continuing the web-poll application and will focus on creating the public interface – “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

A view is a “type” of web page in your Django application that generally serves a specific function and has a specific template. For example, in a blog application, you might have the following 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:

polls/views.py
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:

polls/urls.py
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:

polls/views.py
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:

polls/templates/polls/index.html
{% 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:

polls/views.py
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:

polls/views.py
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:

polls/views.py
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:

polls/templates/polls/detail.html
{{ 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:

polls/views.py
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:

polls/templates/polls/detail.html
<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.

polls/urls.py
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:

polls/templates/polls/index.html
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

para apontar para a view de detalhes d namespace:

polls/templates/polls/index.html
<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.

Back to Top