Pular para o conteúdo
murad.guide

Referência Essencial da Web

Capítulo 29

Django: auth e testes

4 min de leitura 787 palavras 1 gravura

Trabalhe numa cópia da pasta inicio/ desta etapa. Ela já abre ou executa com as instruções do README. Leia o código, mude uma coisa e anote sua previsão antes de recarregar. Compare com a referência na pasta da etapa. Se nada mudar, confira a extensão do arquivo, o arquivo salvo e o caminho aberto. Blocos adicionais do capítulo são experiências separadas; a pasta de referência contém o programa completo desta etapa.

O catálogo já lista livros fictícios. Agora a Biblioteca da Ribeira precisa de sessão, login local, formulário com CSRF e um teste automático. Tudo continua neste computador.

Pasta: exemplos/29-django-auth-e-testes/. Ative .venv, instale, rode migrate e python manage.py runserver 127.0.0.1:8000. Leia, copie, mude um teste ou um texto, preveja, recarregue e confira.

O que você vai aprender

Sessões neste computador

Uma sessão é uma memória curta no servidor, ligada a um cookie no navegador. Se você entra no admin, o Django guarda que a pessoa autenticou. O próximo pedido nesta máquina ainda está logado.

O settings.py já inclui SessionMiddleware e AuthenticationMiddleware. Você não instala sessão à parte neste volume.

O login de estudo é o do admin: http://127.0.0.1:8000/admin/. Use o superusuário fictício do capítulo 28 (ou crie outro com createsuperuser). Sem dados reais.

LOGIN_REDIRECT_URL = "/" manda de volta à home depois de um login de aplicativo. O admin, neste esqueleto, usa a tela própria.

Pare e experimente. Entre no admin. Abra /livros/ noutra aba. Volte ao admin sem digitar a senha de novo. Preveja: a sessão ainda vale neste navegador. Encerre o servidor e abra de novo: pode pedir senha.

Erro comum: achar que “estar logado” publicou a biblioteca. A barra continua 127.0.0.1. Só este PC vê.

Formulário e CSRF

CSRF é uma defesa. Sem ela, outro site poderia mandar um POST no seu nome. O Django coloca um token escondido no form. O middleware CsrfViewMiddleware recusa POST sem esse token.

O admin já traz o token. Se você escrever um form próprio, o template precisa de {% csrf_token %}. Exemplo completo de estudo (não envia dados reais):

<!DOCTYPE html>
<html lang="pt-BR">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Pedido local</title>
  </head>
  <body>
    <h1>Pedido de leitura</h1>
    <form method="post" action="/livros/">
      {% csrf_token %}
      <label for="titulo">Título fictício</label>
      <input id="titulo" name="titulo" required>
      <button type="submit">Enviar neste computador</button>
    </form>
  </body>
</html>

Cada campo tem label. method="post" manda o corpo do pedido. Sem {% csrf_token %} o Django responde 403.

Servidor Django local com sessão e teste

Erro comum: copiar um form HTML puro, sem o token, e POST no Django. A página falha. O token só existe se o arquivo for template renderizado pelo Django, não um HTML aberto com dois cliques.

python manage.py test

Um teste é um pedido falso, no terminal, que confere uma resposta. O arquivo catalogo/tests.py:

from django.test import TestCase
class InicioTest(TestCase):
    def test_inicio_responde(self):
        resposta = self.client.get("/")
        self.assertEqual(resposta.status_code, 200)

self.client.get("/") simula o navegador. 200 significa “página ok”. Rode:

python manage.py test

A saída deve mostrar o teste passando. Não abre o Chrome. Não publica.

Pare e experimente. Troque 200 por 404 só para ver a falha. Preveja “FAILED”. Depois devolva 200 e rode de novo.

Segurança de estudo

No settings.py deste volume os três valores de estudo aparecem assim:

SECRET_KEY = "somente-estudo-local-nao-publique"
DEBUG = True
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]

DEBUG = True mostra erros detalhados. Serve para aprender. Em um site público isso vaza caminhos e segredos. SECRET_KEY é um valor de pasta didática. Nunca reutilize em outro lugar. ALLOWED_HOSTS recusa outros nomes.

O SQLite db.sqlite3 fica na pasta. Não envie esse arquivo com senhas de estudo para a internet. Não cadastre pessoas reais.

Publicar Django exigiria outro ambiente, outra chave e DEBUG desligado. Este volume não faz esse passo. Continuar no PC é o caminho.

Erro comum: copiar o SECRET_KEY deste capítulo para um projeto que alguém vai hospedar. Gere outra chave lá, fora deste material.

Quando python manage.py test passar e o admin abrir só em 127.0.0.1:8000, avance para Express.

Para carregar os dois livros fictícios incluídos, depois de migrate execute python manage.py loaddata livros. O arquivo de origem permanece em catalogo/fixtures/livros.json; o banco criado localmente não acompanha a entrega.

Atividades

  1. O que a sessão guarda neste exemplo e onde o cookie vive?
  2. Por que o form Django precisa de {% csrf_token %}?
  3. O que python manage.py test verifica no arquivo desta pasta?

Respostas comentadas

  1. Guarda que a pessoa autenticou neste servidor local. O cookie fica no navegador desta máquina e aponta para a sessão no Django.
  2. Para o POST ser aceito. Sem o token, um site de fora poderia forjar um envio. O middleware recusa o pedido.
  3. Que a home (/) responde 200. É um teste automático, no terminal, sem publicar e sem dados reais.

O arquivo completo

Referência do exemplo 29-django-auth-e-testes, reproduzida aqui para que você possa conferir sem sair da página.

manage.py

#!/usr/bin/env python
import os
import sys
def main():
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ribeira.settings")
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)
if __name__ == "__main__":
    main()

templates/livros.html

<!DOCTYPE html>

<html lang="pt-BR">
<head><meta charset="utf-8"/><title>Livros</title><style>
/* Apoio de leitura, teclado e tela estreita. */
img{max-width:100%;height:auto} html {overflow-wrap:anywhere} main {min-width:0} input,textarea,select,canvas {max-width:100%;box-sizing:border-box} fieldset {min-width:0}
pre {max-width:100%;overflow:auto} :focus-visible {outline:3px solid #0f3d3e;outline-offset:3px}
.skip {position:absolute;top:-5rem;left:1rem;background:#0f3d3e;color:white;padding:.5rem;z-index:9}.skip:focus {top:.5rem}
table {max-width:100%} td,th {overflow-wrap:anywhere}
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
</style><meta name="viewport" content="width=device-width, initial-scale=1"></head>
<body><a class="skip" href="#conteudo">Pular para o conteúdo</a><main id="conteudo" tabindex="-1">
<h1>Livros</h1>
<ul>{% for livro in livros %}<li>{{ livro.titulo }} — {{ livro.autoria }}</li>{% empty %}<li>Nenhum título ainda. Use o admin.</li>{% endfor %}</ul>
</main></body>
</html>