Introdução
- Até agora, discutimos como construir páginas web simples usando HTML e CSS.
-
Hoje, vamos trabalhar com o framework do Python chamado
Djangopara criar aplicações dinâmicas.
Aplicações Web
Até agora, todas as aplicações web que escrevemos foram estáticas. Isso significa que, sempre que abrimos aquela página, ela aparece exatamente da mesma forma. No entanto, muitos sites que visitamos diariamente mudam toda vez que os acessamos. Se você visitar os sites do Youtube ou do G1, por exemplo, o conteúdo da página hoje é diferente do conteúdo que terá amanhã e já é diferente do conteúdo de ontem.
Para sites grandes como esses, é inviável que os funcionários tenham que editar manualmente grandes arquivos HTML a cada alteração. É aí que os sites dinâmicos são extremamente úteis. Um site dinâmico utiliza uma linguagem de programação (como Python) para gerar HTML e CSS dinamicamente. Nesta aula, aprenderemos a criar nossas primeiras aplicações dinâmicas.
HTTP
Protocolo de Transferência de Hipertexto (HyperText Transfer Protocol ou HTTP), é um protocolo amplamente aceito para a troca de mensagens na internet. Normalmente, as informações são trocadas entre um usuário (cliente) e um servidor.
Nesse protocolo, o cliente envia uma requisição para o servidor (falaremos sobre três tipos de requisição neste curso). Depois, o servidor envia uma resposta HTTP para o usuário que inclui um código de status, uma descrição do conteúdo e outras informações como a versão do HTTP.
Os códigos de status são importantes pois indicam se a requisição foi concluída corretamente ou não. Observe abaixo alguns dos códigos de status e seus significados:
| Código de Status | Significado |
|---|---|
| 200 | OK |
| 404 | Not Found |
| 500 | Internal Server Error |
| 403 | Forbidden |
| 301 | Moved Permanently |
Django
Django é um framework web baseado em Python que nos permite escrever código Python para gerar HTML e CSS de forma dinâmica. A vantagem de usar um framework como o Django é que muito do código já está pronto para usarmos.
Passo Essencial: Criando um Ambiente Virtual (venv)
Antes de instalar o Django ou qualquer outro pacote, é uma prática altamente recomendada criar um "ambiente virtual" (virtual environment). Pense nisso como uma caixa de areia isolada para cada um dos seus projetos. Isso garante que as dependências de um projeto não interfiram nas de outro e mantém a sua instalação principal do Python limpa.
Siga estes passos no seu terminal (Prompt de Comando, PowerShell, ou Terminal no macOS/Linux):
-
Navegue até a pasta do seu projeto:
Use o comando
cdpara entrar no diretório onde você quer que seu projeto viva.cd caminho/para/meu_projeto_django -
Crie o ambiente virtual:
Este comando cria uma nova pasta (geralmente chamada de "venv") contendo uma instalação isolada do Python e do pip.
python -m venv venv(Em alguns sistemas macOS ou Linux, você talvez precise usarpython3em vez depython). -
Ative o ambiente virtual:
Este é o passo crucial. O comando para ativar varia conforme o sistema operacional:
- Windows (Prompt de Comando / CMD):
venv\Scripts\activate - Windows (PowerShell):
venv\Scripts\Activate.ps1(Se encontrar um erro sobre política de execução, tente rodar este comando primeiro:Set-ExecutionPolicy Unrestricted -Scope Process) - macOS e Linux:
source venv/bin/activate
Você saberá que o ambiente está ativo porque o nome dele, (venv), aparecerá no início da linha do seu terminal.
- Windows (Prompt de Comando / CMD):
Quando terminar de trabalhar no seu projeto, você pode desativar o ambiente virtual simplesmente digitando o comando deactivate no terminal.
Instalando o Django
- Para começar, precisamos instalar o Django, o que também exige instalar o pip caso ainda não o tenha instalado (o venv já inclui o pip).
- Uma vez que o ambiente virtual (venv) estiver ativo, você pode executar o comando abaixo no terminal para instalar o Django apenas neste ambiente.
pip install Django
- Execute
django-admin startproject NOME_PROJETOpara criar os arquivos iniciais do projeto. - Navegue até o diretório do projeto com
cd NOME_PROJETO - Você verá que alguns arquivos foram criados automaticamente. Por enquanto, três deles são mais importantes:
manage.py: usado para executar comandos no terminal. Não precisamos editá-lo, mas vamos usá-lo com frequência.settings.py: contém configurações importantes do projeto. Podemos alterar algumas delas eventualmente.urls.py: define para onde o usuário será direcionado ao acessar certas URLs.
-
Inicie o projeto executando
python manage.py runserver. Isso abrirá um servidor de desenvolvimento, que você pode acessar visitando a URL fornecida. Este servidor de desenvolvimento está sendo executado localmente na sua máquina, o que significa que outras pessoas não podem acessar seu site. Isso deve levá-lo a uma página de destino padrão:
-
Agora, precisamos criar uma aplicação. Projetos Django são divididos em uma ou mais aplicações. A maioria dos nossos projetos de exemplo terão apenas uma aplicação, mas sites maiores podem usar várias. Para criar uma, execute:
python manage.py startapp NOME_APP. Isso criará novos diretórios e arquivos que necessitamos, como oviews.py. -
Precisamos instalar a nova aplicação. Para isso, edite
settings.py, role até a listaINSTALLED_APPS, e adicione o nome da aplicação na lista.
No nosso exemplo, criamos o app Users, que será nossa página do usuário.
Rotas
Para iniciar nossa aplicação:
-
No diretório criado para o app, abra o arquivo
views.py. Esse arquivo conterá diferentes views, que podem ser entendidas como páginas que o usuário pode visitar. Para criar nossa primeira view, escreveremos uma função que recebe umrequest. Por enquanto, vamos retornar simplesmente umHttpResponse(uma forma de resposta muito simples que inclui um código de resposta 200 e uma string de texto que pode ser visualizada em um navegador) com “ Oi Usuário!”. Para fazer isso, temos que adicionar ao códigofrom django.http import HttpResponse. Nosso arquivo está como abaixo:from django.shortcuts import render from django.http import HttpResponse # Crie suas views aqui. def index(request): return HttpResponse("Oi Usuário!") -
Agora, precisamos associar essa view a uma URL. Para isso, criaremos um novo arquivo chamado
urls.pyno mesmo diretório deviews.py. Note que já existe umurls.pydo projeto, mas é melhor ter um para cada app separadamente. - No novo
urls.py, criamos uma lista de padrões de URL que o usuário pode acessar enquanto no nosso site:-
Temos que fazer mais alguns imports:
from django.urls import pathnos dá a habilidade de redirecionar URLs, efrom . import viewsimportará quaisquer funções que implementamos emviews.py. - Crie uma lista chamada
urlpatterns -
Para cada URL desejada, adicione um item na lista
urlpatternsque tenha uma chamada da funçãopathcom dois ou três argumentos: Uma string representando o caminho da URL, uma função deviews.pyque desejamos executar quando aquela URL é visitada, e (opcional) um nome para o caminho, no formatoname="meu_caminho". Por exemplo, veja como está nosso código agora:
from django.urls import path from . import views urlpatterns = [ path("", views.index, name="index") ] -
Temos que fazer mais alguns imports:
-
Assim, criamos um
urls.pypara essa aplicação. Agora devemos editar ourls.pydo projeto. Quando você abrir esse arquivo, perceberá que já existe um caminho chamadoadmin, que será explicado na aula seguinte. No momento, desejamos adicionar um caminho para nosso novo app, então adicionamos um item na listaurlpatterns. Isso segue o mesmo padrão dos nossos caminhos anteriores, exceto que, em vez de adicionar uma função deviews.pycomo segundo argumento, queremos incluir todos os caminhos do arquivourls.pyem nossa aplicação. Para fazer isso, escrevemos:include("NOME_APP.urls"), ondeincludeé uma função à qual obtemos acesso importandoincludededjango.urlsconforme mostrado abaixo:from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('user/', include("Users.urls")) ] -
Ao fazer isso, especificamos que, quando um usuário visitar nosso site e adicionar
/userà URL na barra de endereços, ele será redirecionado para os caminhos definidos dentro da nossa nova aplicação.
Agora, quando se iniciar a aplicação usando python manage.py runserver e visitar a URL fornecida, temos seguinte tela:
Isso ocorre porque definimos apenas a URL localhost:8000/user, mas não definimos a URL
localhost:8000 sem nada no final. Então, ao adicionar /user na barra de endereços:
Agora que tivemos sucesso, vamos revisar o que acabou de acontecer para chegarmos a esse ponto:
-
Quando acessamos a URL
localhost:8000/users/, o Django analisou o que veio após a URL base (localhost:8000/) e procurou no arquivourls.pydo projeto por um padrão que correspondesse aUsers. - Ele encontrou a extensão que definimos e verificou que, ao encontrá-la, deveria incluir o arquivo
urls.pyda nossa aplicação. -
Em seguida, o Django ignorou as partes da URL já utilizadas no redirecionamento (
localhost:8000/users/, ou tudo isso) e procurou nourls.pyda aplicação um padrão que correspondesse ao restante da URL. -
Ele encontrou que o único caminho definido até agora (
"") correspondia ao que restava da URL, e então nos direcionou à função index deviews.pyassociada a esse caminho. - Por fim, o Django executou essa função dentro de
views.py, e retornou o resultado (HttpResponse("Hello, world!")) para o navegador.
Agora, se quisermos, podemos alterar a função index dentro de views.py
para retornar o que quisermos! Podemos até mesmo manter variáveis e fazer cálculos dentro da função antes de retornar algo.
Agora, vamos ver como podemos adicionar mais de uma view à nossa aplicação. Podemos seguir muitos dos mesmos passos para criar páginas que cumprimentam alguns usuários específicos.
Dentro de views.py:
from django.shortcuts import render
from django.http import HttpResponse
# Crie suas views aqui.
def index(request):
return HttpResponse("Oi Usuário!")
def kaiky(request):
return HttpResponse("Oi Kaiky!")
def rafael(request):
return HttpResponse("Oi Rafael!")
Dentro de urls.py (na aplicação, não no projeto)
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("kaiky", views.kaiky, name="kaiky"),
path("rafael", views.rafael, name="rafael")
]
Agora, o site permanece igual quando visitamos localhost:8000/user,
mas temos páginas diferentes quando adicionamos kaiky ou rafael no final da URL:
Muitos sites são parametrizados por itens incluídos na URL. Por exemplo, acessar www.youtube.com/CodeLabBR
mostrará todos os vídeos publicados pelo CodeLab, e acessar www.instagram.com/uspcodelab/ levará você à página do CodeLab no Instagram.
Você pode até encontrar seu próprio perfil do Instagram navegando até www.instagram.com/SEU_@/!
Ao pensar em como isso é implementado, parece impossível que sites como o Youtube e o Instagram tenham um caminho de URL individual para cada usuário.
Então, vamos ver como podemos criar um caminho mais flexível. Vamos começar adicionando uma função mais geral, chamada usuario,
ao views.py:
def usuario(request, nome):
return HttpResponse(f"Oi {nome}!")
Essa função recebe não apenas a requisição, mas também um argumento adicional com o nome do usuário, e retorna uma resposta HTTP personalizada baseada nesse nome.
Em seguida, precisamos criar um caminho mais flexível em urls.py:
path("<str:nome>", views.usuario, name="usuario")
Essa é uma nova sintaxe, mas, essencialmente, estamos dizendo que não estamos mais procurando uma palavra ou nome específico na URL, mas qualquer string que o usuário possa digitar. Agora, podemos testar o site com algumas URLs diferentes:
Podemos até deixar isso um pouco mais bonito, melhorando a função usuario
com a função capitalize do Python, que coloca a primeira letra em maiúsculo:
def usuario(request, nome):
return HttpResponse(f"Oi {nome.capitalize()}!")
Essa é uma ótima ilustração de como qualquer funcionalidade do Python pode ser usada no Django antes de ser retornada.
Templates
Até agora, nossas respostas HTTP foram apenas texto, mas podemos incluir qualquer elemento HTML que quisermos!
Por exemplo, eu poderia decidir retornar um cabeçalho azul em vez de apenas texto na nossa função
index:
def index(request):
return HttpResponse("<h1 style=\"color:blue\">Oi Usuário!</h1>")
Escrever uma página HTML inteira dentro do views.py.
se tornaria muito tedioso. Além disso, isso é uma prática ruim, já que queremos manter partes separadas do nosso
projeto em arquivos separados sempre que possível.
É por isso que agora vamos introduzir os templates do Django, que nos permitirão escrever HTML e CSS em arquivos separados e renderizar esses arquivos usando o Django. A sintaxe que usaremos para renderizar um template é a seguinte:
def index(request):
return render(request, "Users/index.html")
Agora, precisaremos criar esse template. Para isso, vamos criar uma pasta chamada
templates dentro do nosso app, depois criar uma pasta chamada
Users (ou o nome do seu app)
dentro dela, e então adicionar um arquivo chamado index.html.
Em seguida, adicionaremos o que quisermos nesse novo arquivo:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<title>Usuário</title>
</head>
<body>
<h1>Oi Usuário!</h1>
<p>Isso é uma página de usuário.</p>
</body>
</html>
Agora, quando visitarmos a página principal da nossa aplicação, veremos que o cabeçalho e o título foram atualizados:
Além de escrever algumas páginas HTML estáticas, também podemos usar a linguagem de templates do Django
para alterar o conteúdo dos nossos arquivos HTML com base na URL visitada. Vamos testar isso alterando nossa função
usuario de antes:
def usuario(request, nome):
return render(request, "Users/usuario.html", {"nome": nome.capitalize()})
Observe que passamos um terceiro argumento para a função render
chamado de contexto. Nesse contexto, podemos fornecer informações que queremos que estejam disponíveis dentro dos nossos
arquivos HTML. Esse contexto assume a forma de um dicionário Python. Agora, podemos criar um arquivo
usuario.html na pasta de templates:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<title>{{ nome }}</title>
</head>
<body>
<h1>Oi {{ nome }}</h1>
<p>Essa é a página de {{ nome }}.</p>
</body>
</html>
Você deve ter notado que usamos uma nova sintaxe: chaves duplas.
Essa sintaxe nos permite acessar variáveis que fornecemos no argumento de contexto. Agora, ao testarmos:
Vimos como podemos modificar nossos templates HTML com base no contexto fornecido. No entanto, a linguagem de templates do Django é ainda mais poderosa do que isso, então vamos dar uma olhada em algumas outras formas em que ela pode ser útil:
Condicionais
Podemos querer alterar o que é exibido em nosso site dependendo de algumas condições.
Por exemplo, para nossa página de usuários, gostaríamos que algo indicasse que uma certa pessoa é uma administradora do curso.
Para fazermos isso, vamos modificar nossa função usuarios e o arquivo usuarios.html.
Antes de prosseguir, vamos descobrir como verificar se é uma pessoa é ou não um administrador do curso. Suponha que, no nosso caso de exemplo, apenas o primeiro nome da pessoa importa tal que os administradores do curso são Kaiky, Ryan, Vitor e Rafael.
- Podemos usar esse conhecimento para construir uma expressão booleana que será avaliada como True se, e somente se, hoje for o Dia de Ano Novo:
nome in [vetor com nome de adms] - Agora que temos uma expressão para verificar se um usuário é administrador, podemos atualizar
views.py:
def usuario(request, nome):
nome = nome.capitalize()
administradores = ["Kaiky","Rafael", "Ryan", "Vitor"]
return render(request, "Users/usuario.html", {"nome": nome, "adm": nome in administradores})
Note que agora passamos dois "argumentos" para usuarios.html, nome(string) e adm(booleano).
Em seguida, vamos modificar nosso template usuarios.html.
Dentro desse arquivo, escreveremos algo assim:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<title>{{ nome }}</title>
</head>
<body>
<h1>Oi {{ nome }}!</h1>
<p>Essa é a página de {{ nome }}.</p>
{% if adm %}
<p>{{ nome }} é um administrador.</p>
{% else %}
<p>{{ nome }} não é um administrador.</p>
{% endif %}
</body>
</html>
No código acima, repare que, quando queremos incluir lógica nos nossos arquivos HTML,
usamos {% e
%} como tags de abertura e fechamento para declarações lógicas.
Também note que a linguagem de template do Django exige que incluamos uma tag de encerramento indicando que o bloco
if-else terminou.
Agora, podemos abrir a nossa página para ver o resultado.
Para entender melhor o que está acontecendo nos bastidores, vamos inspecionar o elemento dessa página:
Observe que o HTML que está sendo realmente enviado ao seu navegador inclui apenas a parte que indica que o Kaiky é um administrador,
o que significa que o Django está usando o template HTML que escrevemos para criar um novo arquivo HTML que possuí apenas a parte verdadeira do bloco if, e só então o envia ao navegador.
Se inspecionarmos a página do Kim, veremos que o caso oposto será exibido:
Estilização
Se quisermos adicionar um arquivo CSS — que é um arquivo estático, pois não muda — primeiro criaremos uma pasta chamada
static dentro do nosso app Users, depois uma pasta
Users dentro dela, e então um arquivo
styles.css dentro dessa pasta.
Nesse arquivo, podemos adicionar qualquer estilo que quisermos, assim como fizemos na primeira aula:
h1 {
font-family: sans-serif;
font-size: 50px;
color: orange;
}
Agora, para incluir esse estilo no nosso arquivo HTML, adicionamos a linha {% load static %} no topo do nosso template HTML,
o que informa ao Django que queremos ter acesso aos arquivos dentro da pasta static.
Em vez de escrever manualmente o link para o arquivo de estilo, como fizemos antes, usaremos uma sintaxe específica do Django:
<link rel="stylesheet" href="{% static 'Users/styles.css' %}" >
De forma que nosso HTML tenha forma:
{% load static %}
<!DOCTYPE html>
<html lang="pt-br">
<head>
<title>{{ nome }}</title>
<link rel="stylesheet" href="{% static 'Users/styles.css' %}" >
</head>
<body>
<h1>Oi {{ nome }}!</h1>
<p>Essa é a página de {{ nome }}.</p>
{% if adm %}
<p>{{ nome }} é um administrador.</p>
{% else %}
<p>{{ nome }} não é um administrador.</p>
{% endif %}
</body>
</html>
Agora, se reiniciarmos o servidor, veremos que as mudanças de estilo foram aplicadas de fato:
Nota: a depender da versão, é possível ter algum erro envolvendo o import do static.
Caso isso aconteça, vá ao settings.py do projeto, procure pela linha STATIC_URL = 'static/' e coloque como STATIC_URL = '/static/'
Tarefas
Agora, vamos pegar o que aprendemos até aqui e aplicar em um mini-projeto: criar uma lista de cursos.
Vamos começar, mais uma vez, criando um novo app Cursos:
- Execute
python manage.py startapp Cursosno terminal. -
Edite o arquivo
settings.py, adicionando "Cursos" à listaINSTALLED_APPS
-
Edite o arquivo
urls.pydo projeto e inclua um path semelhante ao que criamos para o appUserspath('cursos/', include("Cursos.urls")) -
Crie outro arquivo
urls.pydentro do diretório do novo app e atualize-o para incluir um caminho semelhante ao index doUsers:from django.urls import path from . import views urlpatterns = [ path("", views.index, name="index") ]
Agora, vamos começar tentando simplesmente criar uma lista de cursos e exibi-la em uma página.
Vamos criar uma lista Python no topo do arquivo views.py
onde armazenaremos nossos cursos. Depois, podemos atualizar a função index
para renderizar um template e fornecer nossa lista recém-criada como contexto.
from django.shortcuts import render
cursos = ["dev.learn", "dev.hire", "hackfools"]
# Create your views here.
def index(request):
return render(request, "Cursos/index.html", {"cursos": cursos})
Agora, vamos trabalhar na criação do nosso arquivo HTML de template:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<title>Cursos</title>
</head>
<body>
<h1>Cursos</h1>
<ul>
{% for curso in cursos %}
<li>{{ curso }}</li>
{% endfor %}
</ul>
</body>
</html>
Repare que conseguimos iterar sobre nossas tarefas usando uma sintaxe semelhante às condicionais que vimos anteriormente,
e também parecida com um laço for em Python. Quando acessamos a página de tarefas agora, conseguimos ver nossa lista sendo exibida:
Formulários
Agora que conseguimos ver todos os cursos como uma lista, talvez queiramos poder adicionar novos cursos.
Para isso, vamos começar a explorar o uso de forms (formulários) para atualizar uma página da web.
Vamos começar adicionando outra função em views.py
que irá renderizar uma página com um formulário para adicionar uma novo curso:
def novo_curso(request):
return render(request, "Cursos/novo_curso.html")
Em seguida, certifique-se de adicionar outro path ao urls.py:
path("novo", views.novo_curso, name="novo_curso")
Agora, vamos criar nosso arquivo novo_curso.html,
que é bem parecido com o index.html,
exceto que, no corpo, incluiremos um formulário em vez de uma lista:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<title>Cursos</title>
</head>
<body>
<h1>Adicionar Curso:</h1>
<form action="">
<input type="text" name="curso">
<input type="submit">
</form>
</body>
</html>
No entanto, o que acabamos de fazer não é necessariamente o melhor design, pois repetimos a maior parte do HTML
em dois arquivos diferentes. A linguagem de template do Django nos oferece uma forma de eliminar esse design ruim:
herança de template.
Isso nos permite criar um arquivo layout.html na pasta de templates
que conterá a estrutura geral da nossa página:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<title>Cursos</title>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
Repare que novamente usamos {%...%} para indicar algum
tipo de lógica não-HTML e, nesse caso, estamos dizendo ao Django para preencher esse "bloco" com algum conteúdo
vindo de outro arquivo. Agora, podemos alterar nossos outros dois arquivos HTML para ficarem assim:
index.html:
{% extends "Cursos/layout.html" %}
{% block body %}
<h1>Cursos</h1>
<ul>
{% for curso in cursos %}
<li>{{ curso }}</li>
{% endfor %}
</ul>
{% endblock %}
novo_curso.html:
{% extends "Cursos/layout.html" %}
{% block body %}
<h1>Adicionar Curso:</h1>
<form action="">
<input type="text" name="curso">
<input type="submit">
</form>
{% endblock %}
Perceba como agora conseguimos eliminar grande parte do código repetido ao estender nosso arquivo de layout. Assim, nossa página index continua a mesma e agora temos também uma página add:
Em seguida, não é ideal ter que digitar "/novo" na URL toda vez que quisermos adicionar uma novo curso,
então provavelmente vamos querer adicionar alguns links entre as páginas. Em vez de codificar os links diretamente,
agora podemos usar a variável name que atribuímos a cada rota no
urls.py e criar um link assim:
<a href="{% url 'novo_curso' %}">Adicionar novo curso</a>
Onde 'novo_curso' é o nome daquela rota. Podemos fazer algo semelhante em nosso novo_curso.html:
<a href="{% url 'index' %}">Adicionar novo curso</a>
Isso, no entanto, pode criar um problema, já que temos algumas rotas chamadas
index em diferentes apps. Podemos resolver isso indo em cada
um dos arquivos urls.py dos nossos apps e adicionando uma variável
app_name para que os arquivos agora fiquem parecidos com isto:
from django.urls import path
from . import views
app_name = "Cursos"
urlpatterns = [
path("", views.index, name="index"),
path("novo", views.novo_curso, name="novo_curso")
]
Com isso, podemos mudar nossos links de simplesmente index e
novo_curso para
Cursos:index e
Cursos:novo_curso
<a href="{% url 'Cursos:index' %}">Adicionar novo curso</a>
<a href="{% url 'Cursos:novo_curso' %}">Adicionar novo curso</a>
Agora, vamos trabalhar para garantir que o formulário realmente faça algo quando o usuário o submeter.
Podemos fazer isso adicionando um action no formulário que criamos em
novo_curso.html:
<form action="{% url 'Cursos:novo_curso' %}" method="post">
Isso significa que, ao submeter o formulário, seremos redirecionados novamente à URL
/novo. Aqui, especificamos que usaremos o método
post em vez do método get que é o mais apropriado sempre que um formulário pode alterar o estado da página.
Precisamos adicionar um pouco mais ao nosso formulário agora, porque o Djan go exigeum token para prevenir ataques de falsificação de solicitação entre sites (Cross-Site Request Forgery, ou CSRF). Esse é um tipo de ataque onde um usuário malicioso tenta enviar uma requisição para seu servidor a partir de outro site que não é o seu. Isso pode ser um problema sério para alguns sites. Suponha, por exemplo, que um site de banco tenha um formulário para um usuário transferir dinheiro para outro. Seria catastrófico se alguém conseguisse submeter uma transferência a partir de fora do site do banco!
Para resolver esse problema, quando o Django envia uma resposta renderizando um template, ele também fornece um token CSRF que é único para cada nova sessão no site. Então, quando uma requisição é submetida, o Django verifica se o token CSRF associado à requisição corresponde a um que foi fornecido recentemente. Assim, se um usuário malicioso em outro site tentar submeter uma requisição, ela será bloqueada devido a um token CSRF inválido. Essa validação CSRF é integrada ao Middleware do Django, que pode intervir no processamento de requisições e respostas de uma aplicação Django. Não entraremos em mais detalhes sobre Middleware neste curso, mas vale a pena consultar a documentação se estiver interessado!
Para incorporar essa proteção no nosso código, devemos adicionar a linha {% csrf_token %} ao nosso formulário em
novo_curso.html.
{% extends "Cursos/layout.html" %}
{% block body %}
<h1>Adicionar Curso:</h1>
<form action="{% url 'Cursos:novo_curso' %}" method="post">
{% csfr_token %}
<input type="text" name="curso">
<input type="submit">
</form>
{% endblock %}
Essa linha adiciona um campo oculto com o token CSRF fornecido pelo Django, de forma que, ao recarregarmos a página,
parece que nada mudou. No entanto, se inspecionarmos o elemento, notaremos que um novo campo de entrada foi adicionado:
Formulários do Django
Embora possamos criar formulários escrevendo HTML puro, como acabamos de fazer, o Django fornece uma maneira ainda mais
fácil de coletar informações de um usuário: os Formulários do Django.
Para usar esse método, adicionaremos o seguinte no topo do views.py
para importar o módulo forms:
from django import forms
Agora, podemos criar um novo formulário dentro de views.py
criando uma classe Python chamada NewTaskForm:
class NewTaskForm(forms.Form):
curso = forms.CharField(label="Novo Curso")
Agora, vamos entender o que está acontecendo dentro dessa classe:
-
Dentro dos parênteses após
NewTaskForm, vemos que temosforms.Form. Isso acontece porque nosso novo formulário herda de uma classe chamadaFormque está incluída no móduloforms. Já vimos como a herança pode ser usada na linguagem de templates do Django. Este é mais um exemplo de como a herança é usada para partir de uma descrição mais geral (a classeforms.Form) e refiná-la para o que desejamos (nosso novo formulário). A herança é uma parte fundamental da Programação Orientada a Objetos, que não discutiremos em profundidade neste curso, mas existem muitos recursos online disponíveis para quem quiser aprender mais sobre o assunto! - Dentro dessa classe, podemos especificar quais informações gostaríamos de coletar do usuário — neste caso, o nome de um curso.
-
Especificamos que isso deve ser um campo de texto usando
forms.CharField, mas há muitos outros tipos de campos disponíveis no módulo de formulários do Django que podemos usar. -
Dentro desse
CharField, especificamos umlabel, que será exibido para o usuário quando ele carregar a página. Umlabelé apenas um dos muitos argumentos que podemos passar para um campo de formulário.
Agora que criamos a classe NewTaskForm,
podemos incluí-la no contexto ao renderizar a página novo_curso
def novo_curso(request):
return render(request, "Cursos/novo_curso.html", {"form": NewTaskForm()})
Agora, dentro do novo_curso.html,
podemos substituir nosso campo de entrada (input) pelo formulário que acabamos de criar:
{% extends "Cursos/layout.html" %}
{% block body %}
<h1>Adicionar Curso:</h1>
<form action="{% url 'Cursos:novo_curso' %}" method="post">
{% csfr_token %}
{% form %}
<input type="submit">
</form>
<a href="{% url 'Cursos:index' %}">Ver cursos</a>
{% endblock %}
Existem várias vantagens em usar o módulo forms
em vez de escrever manualmente um formulário HTML:
- Se quisermos adicionar novos campos ao formulário, podemos simplesmente adicioná-los em
views.pysem precisar escrever HTML adicional. - O Django executa automaticamente a validação no lado do cliente, ou seja, no próprio navegador do usuário. Isso significa que ele não permitirá que o usuário envie o formulário se ele estiver incompleto.
- O Django também fornece validação no lado do servidor, ou seja, após os dados do formulário chegarem ao servidor.
- Na próxima aula, começaremos a usar models para armazenar informações, e o Django torna muito simples criar um formulário baseado em um modelo.
Agora que temos um formulário configurado, vamos trabalhar no que acontece quando um usuário clica no botão de envio.
Quando um usuário acessa a página de adição clicando em um link ou digitando a URL, ele envia uma requisição
GET para o servidor, que já tratamos na nossa função
novo_curso. No entanto, quando ele envia um formulário, ele faz uma requisição
POST para o servidor, que no momento ainda não está sendo tratada na função
novo_curso. Podemos tratar o método
POST adicionando uma condição baseada no argumento
request que nossa função recebe. Os comentários no código
abaixo explicam o propósito de cada linha:
def novo_curso(request):
if (request.method == "POST"):
# Salva em um form os dados submetidos pelo usuário
form = NewTaskForm(request.POST)
# Checa se os dados do form são válidos (server-side)
if (form.is_valid()):
#Isola o curso da versão 'limpa' dos dados do form
curso = form.cleaned_data["curso"]
# Adiciona o novo curso na lista de cursos
cursos.append(curso)
# Redireciona o usuário para a lista de cursos
return HttpResponseRedirect(reverse("Cursos:index"))
else:
# Se o formulário é inválido, re-renderiza a página com informações existentes.
return render(request, "Cursos/novo_curso.html", {"form": form})
return render(request, "Cursos/novo_curso.html", {"form": NewTaskForm()})
Observação - para redirecionar o usuário após um envio bem-sucedido, precisaremos de mais alguns imports:
from django.urls import reverse
from django.http import HttpResponseRedirect
Finalmente, podemos adicionar um curso:
Sessões
Neste ponto, conseguimos construir uma aplicação que nos permite adicionar cursos a uma lista crescente. No entanto, pode ser problemático armazenar essas tarefas como uma variável global, pois isso significa que todos os usuários que acessam a página veem exatamente a mesma lista. Para resolver esse problema, vamos usar uma ferramenta conhecida como sessões.
Sessões são uma maneira de armazenar dados únicos no lado do servidor para cada nova visita a um site.
Para usar sessões em nossa aplicação, primeiro vamos deletar a variável global cursos,
depois alterar nossa função index, e por fim, garantir que em
todos os lugares onde usamos a variável cursos ela seja substituida por
request.session["cursos"]
def index(request):
# Verifica se já existe uma chave "cursos" na sessão atual
if ("cursos" not in request.session):
# Se não estiver, cria uma lista nova
request.session["cursos"] = []
return render(request, "Cursos/index.html", {"cursos": request.session["cursos"]})
def novo_curso(request):
if (request.method == "POST"):
# Salva em um form os dados submetidos pelo usuário
form = NewTaskForm(request.POST)
# Checa se os dados do form são válidos (server-side)
if (form.is_valid()):
#Isola o curso da versão 'limpa' dos dados do form
curso = form.cleaned_data["curso"]
# Adiciona o novo curso na lista de cursos
request.session["cursos"] += [curso]
# Redireciona o usuário para a lista de cursos
return HttpResponseRedirect(reverse("Cursos:index"))
else:
# Se o formulário é inválido, re-renderiza a página com informações existentes.
return render(request, "Cursos/novo_curso.html", {"form": form})
return render(request, "Cursos/novo_curso.html", {"form": NewTaskForm()})
Assim, temos que as telas por sessão serão:
Por fim, antes que o Django possa armazenar esses dados, devemos executar o seguinte comando no terminal:
python manage.py migrate in the terminal.
Na próxima semana falaremos mais sobre o que é uma migration (migração), mas por enquanto, basta saber que esse comando permite que o Django armazene sessões.
Isso é tudo por esta aula!
Na próxima, trabalharemos com o uso do Django para armazenar, acessar e manipular dados.