EBOOK Desenvolvimento Web Com Python e Django
EBOOK Desenvolvimento Web Com Python e Django
DESENVOLVIMENTO WEB
COM PYTHON E DJANGO
SUMÁRIO
CAPÍTULO 1: INTRODUÇÃO 4
FLUXO DE UMA REQUISIÇÃO 6
CAPÍTULO 2: INSTALAÇÃO 8
HELLO WORLD, DJANGO! 9
INTRODUÇÃO
INSTALAÇÃO
import django
print(django.get_version())
Available subcommands:
[django]
check
compilemessages
createcachetable
dbshell
diffsettings
dumpdata
flush
inspectdb
loaddata
makemessages
makemigrations
migrate
runserver
sendtestemail
shell
showmigrations
sqlflush
sqlmigrate
sqlsequencereset
squashmigrations
startapp
startproject
test
testserver
Por ora, estamos interessados no comando startproject que cria um
novo projeto com a estrutura de diretórios certinha para começarmos a
desenvolver!
/helloworld
- __init__.py
- asgi.py
- settings.py
- urls.py
- wsgi.py
- manage.py
Uma aplicação Web que faz alguma coisa, por exemplo - um blog, um banco de
dados de registros públicos ou um aplicativo de pesquisa. Já um projeto é uma
coleção de configurações e apps para um website em particular.
Ele nos ajuda a criar os arquivos e diretórios necessários para tal objetivo.
Na raíz do projeto, execute:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'helloworld',
'website'
]
Por fim, você deve estar com a estrutura de diretórios da seguinte forma:
CONCLUSÃO DO CAPÍTULO
CAMADA MODEL
ONDE ESTAMOS…
No primeiro capítulo, tratamos de conceitos introdutórios do framework,
uma visão geral da sua arquitetura
CAMADA MODEL
Vamos começar pelo básico: pela definição de modelo!
● Nome
● Sobrenome
● CPF
● Tempo de serviço
● Remuneração
Agora, é necessário traduzir isso para código Python para que o Django
possa entender.
Nele, nós iremos descrever cada atributo (nome, sobrenome, CPF e etc)
como um campo (ou Field) da nossa classe de Modelo.
class Funcionario(models.Model):
nome = models.CharField(
max_length=255,
null=False,
blank=False
)
sobrenome = models.CharField(
max_length=255,
null=False,
blank=False
)
cpf = models.CharField(
max_length=14,
null=False,
blank=False
)
tempo_de_servico = models.IntegerField(
default=0,
null=False,
blank=False
)
remuneracao = models.DecimalField(
max_digits=8,
decimal_places=2,
null=False,
blank=False
)
objetos = models.Manager()
O COMANDO makemigrations
Vamos entender o que eu acabei de dizer: toda vez que você faz
uma alteração em seu modelo, é necessário que ela seja aplicada a estrutura
de tabelas do banco de dados.
O COMANDO migrate
Para que o Django aplique essa Migrações, são necessárias três coisas,
basicamente:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
Para isso, vamos para a raíz do projeto (onde está o script manage.py) e
executamos: python manage.py migrate. A saída deve ser:
Operations to perform:
Apply all migrations: admin, auth, contenttypes, helloworld, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add...
Applying contenttypes.0002_remove_content_ty...
Applying auth.0002_alter_permission_name_max...
Applying auth.0003_alter_user_email_max_leng...
Applying auth.0004_alter_user_username_opts...
Applying auth.0005_alter_user_last_login_nul...
Applying auth.0006_require_contenttypes_0002...
Applying auth.0007_alter_validators_add_erro...
Applying auth.0008_alter_user_username_max_l...
Applying auth.0009_alter_user_last_name_max_...
Applying helloworld.0001_initial... OK
Applying sessions.0001_initial... OK
Pois então, cada app desses contém seus próprios modelos e migrations.
Por isso que ao executar o comando migrate o Django aplicou tudo que estava
aguardando ser aplicado. Sacou?!
Com a execução do comando migrate, o Django irá executar diversos
comandos SQL para criar a estrutura necessária para execução da nossa
aplicação. Uma delas é a tabela referente ao nosso modelo Funcionário, similar
à:
Para ver os dados de cada tabela, vá para a aba “Navegar dados”, escolha
nossa tabela helloworld_funcionario e…
funcionario = Funcionario(
nome='Marcos',
sobrenome='da Silva',
cpf='015.458.895-50',
tempo_de_servico=5,
remuneracao=10500.00
)
funcionario.save()
E…. Pronto!
print(funcionario.id)
funcionarios = Funcionario.objetos.all()
Se lembra do tal Manager que falamos lá em cima? Então, um Manager é a
interface na qual as operações de busca são definidas para o seu modelo.
funcionarios = Funcionario.objetos
.exclude(nome="Marcos")
.filter(tempo_de_servico__gt=3)
.filter(remuneracao__lt=5000.00)
.all()
# Agora, o deletamos!
funcionario.delete()
# Salvamos as alterações
funcionario.save()
CONCLUSÃO DO CAPÍTULO
CAMADA VIEW
Então vamos nessa, que esse capítulo está repleto de código e muito
conteúdo!
Mas antes, você já sabe: vamos nos situar para saber onde estamos,
dentro do ciclo de vida de uma Requisição HTTP dentro do Framework Django.
ONDE ESTAMOS…
Primeiramente, vamos nos situar:
CAMADA VIEW
A principal responsabilidade desta camada é a de processar as
requisições vindas dos usuários, formar uma resposta e enviá-la de volta ao
usuário. E é nesta camada que residem as lógicas de negócio da nossa
aplicação.
urlpatterns = [
# Inclui as URLs do app website
path('', include('website.urls', namespace='website')),
# Interface administrativa
path('admin/', admin.site.urls),
]
Pode parecer complicado, mas ali embaixo, quando tratarmos mais sobre
Views, vai fazer mais sentido. A configuração do URLConf é bem simples, basta
definirmos qual função ou View irá processar requisições de tal URL. Por
exemplo, queremos que:
app_name = 'website'
Para entender as diferenças das Funções e das Class Based Views, vamos
fazer um exemplo. Suponha você quer criar uma página com a listagem de
todos os funcionários. Utilizando funções, você poderia chegar a esse objetivo
da seguinte forma:
def lista_funcionarios(request):
# Primeiro, buscamos os funcionarios
funcionarios = Funcionario.objetos.all()
# Incluímos no contexto
contexto = {'funcionarios': funcionarios}
class ListaFuncionarios(ListView):
template_name = "templates/funcionarios.html"
model = Funcionario
context_object_name = "funcionarios"
Perceba que você não precisou descrever a lógica para buscar a lista de
funcionários?
<table>
<tbody>
{% for funcionario in funcionarios %}
<tr>
<td>{{ funcionario.nome }}</td>
<td>{{ funcionario.sobrenome }}</td>
<td>{{ funcionario.remuneracao }}</td>
<td>{{ funcionario.tempo_de_servico }}</td>
</tr>
{% endfor %}
</tbody>
</table>
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('lista_funcionarios'))
O fluxo é o seguinte:
Deu para perceber que o objeto request é essencial nas nossas Views,
né?
Certo?!
urlpatterns = [
path('', TemplateView.as_view(template_name="index.html")),
]
class IndexTemplateView(TemplateView):
template_name = "index.html"
urlpatterns = [
path('', IndexTemplateView.as_view(), name='index'),
]
ListView
class FuncionarioListView(ListView):
template_name = "website/lista.html"
model = Funcionario
context_object_name = "funcionarios"
UpdateView
No nosso caso:
class FuncionarioUpdateView(UpdateView):
template_name = 'atualiza.html'
model = Funcionario
fields = [
'nome',
'sobrenome',
'cpf',
'tempo_de_servico',
'remuneracao'
]
urlpatterns = [
# Utilizando o {id} para buscar o objeto
path(
'funcionario/<id>',
FuncionarioUpdateView.as_view(),
name='atualiza_funcionario'),
● /funcionario/vinicius
● /funcionario/175
class FuncionarioUpdateView(UpdateView):
template_name = "atualiza.html"
model = Funcionario
fields = '__all__'
context_object_name = 'funcionario'
if id is not None:
# Busca o funcionario apartir do id
funcionario = Funcionario.objects.filter(id=id).first()
DeleteView
class FuncionarioDeleteView(DeleteView):
template_name = "website/exclui.html"
model = Funcionario
context_object_name = 'funcionario'
success_url = reverse_lazy(
"website:lista_funcionarios"
)
urlpatterns = [
path(
'funcionario/excluir/<pk>',
FuncionarioDeleteView.as_view(),
name='deleta_funcionario')
]
<form method="post">
{% csrf_token %}
Você tem certeza que quer excluir o funcionário <b>{{ funcionario.nome }}</b>?
<br><br>
<button type="button">
<a href="{% url 'lista_funcionarios' %}">Cancelar</a>
</button>
<button>Excluir</button>
</form>
Algumas colocações:
class FuncionarioCreateView(CreateView):
template_name = "website/cria.html"
model = Funcionario
form_class = InsereFuncionarioForm
success_url = reverse_lazy("website:lista_funcionarios")
urlpatterns = [
path(
'funcionario/cadastrar/',
FuncionarioCreateView.as_view()
name='cadastra_funcionario'),
]
FORMS NO DJANGO
Podemos utilizar o formulário do Django nas páginas HTML de duas
formas. A primeira, mostra o formulário inteiro ”cru”, isto é, sem formatação e
sem estilo, conforme o Django nos entrega.
Podemos utilizá-lo no nosso template da seguinte forma:
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Cadastrar</button>
</form>
<form method="post">
{% csrf_token %}
<button type="submit">Cadastrar</button>
</form>
Nesse template:
class InsereFuncionarioForm(forms.Form):
nome = forms.CharField(
label='Nome do Funcionário',
max_length=100
)
Neste formulário:
class InsereFuncionarioForm(forms.Form)
nome = forms.CharField(
required=True,
max_length=255
)
sobrenome = forms.CharField(
required=True,
max_length=255
)
cpf = forms.CharField(
required=True,
max_length=14
)
tempo_de_servico = forms.IntegerField(
required=True
)
remuneracao = forms.DecimalField()
Affff, mas o Model e o Form são quase iguais… Terei que reescrever os
Claro que não, jovem! Por isso o Django nos presenteou com o incrível
ModelForm!
class InsereFuncionarioForm(forms.ModelForm):
class Meta:
# Modelo base
model = Funcionario
Ficou confuso?
Então vamos ver um exemplo que utiliza todos os atributos e ainda
adiciona novos campos ao formulário:
class InsereFuncionarioForm(forms.ModelForm)
chefe = forms.BooleanField(
label='Este Funcionário exerce função de Chefia?',
required=True,
)
biografia = forms.CharField(
label='Biografia',
required=False,
widget=forms.TextArea
)
class Meta:
# Modelo base
model = Funcionario
MIDDLEWARES
Middlewares são trechos de códigos que podem ser executados antes ou
depois do processamento de Requisições/Respostas pelas Views da nossa
aplicação. É uma forma que nós temos para alterar como o Django processa
algum dado de entrada ou de saída.
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
<li>
<a href="{% url 'profile' id=user.id %}">
Olá, {{ user.email }}
</a>
</li>
def middleware_simples(get_response):
response = get_response(request)
return response
return middleware
E como Classe:
class MiddlewareSimples:
def __init__(self, get_response):
self.get_response = get_response
response = self.get_response(request)
return response
O MÉTODO process_view
O MÉTODO process_exception
● request é o objeto HttpRequest
● exception é a exceção que foi lançada pela view.
O MÉTODO process_template_response
● request é um objeto HttpRequest.
● response é o objeto TemplateResponse retornado pela view ou por outro
middleware.
class FiltraIPMiddleware:
# IP do usuário
ip = request.META.get('REMOTE_ADDR')
# Verifica se o IP do está na lista de IPs autorizados
if ip not in ips_autorizados:
# Se usuário não autorizado > HTTP 403 (Não Autorizado)
return HttpResponseForbidden("IP não autorizado")
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# Nosso Middleware
'helloworld.middlewares.FiltraIPMiddleware',
]
CONCLUSÃO DO CAPÍTULO
CAMADA TEMPLATE
ONDE ESTAMOS…
Primeiro, vamos relembrar onde estamos no fluxo de
Requisição/Resposta da nossa aplicação Django:
Agora, estamos na camada que faz a interface do nosso código
Python/Django com o usuário, interagindo, trocando informações e captando
dados de input.
DEFINIÇÃO DE TEMPLATE
Basicamente, um template é um arquivo base que pode ser
transformado em outro arquivo (um arquivo HTML, um CSS, um CSV, etc),
através do processo de interpolação de código.
Pense o seguinte: você talvez já saiba que HTML não tem estruturas de
repetição como o for ou while do Python, correto?
<h1>Teste de interpolação</h1>
{% block conteudo %}
<h1>{{ section.title }}</h1>
{% for f in funcionarios %}
<h2>
<a href="{% url 'website:funcionario_detalhe' pk=f.id %}">
{{ funcionario.nome|upper }}
</a>
</h2>
{% endfor %}
{% endblock %}
CONFIGURAÇÃO
O nosso arquivo de configuração settings.py contém a seguinte
configuração, que define qual Engine (também chamada de Backend) fará o
processamento dos templates da nossa aplicação:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {},
},
]
Por ora, vamos utilizar as configurações padrão “de fábrica” pois elas já
nos atendem. Agora, vamos ver sobre a tal Django Template Language!
Complicado, hein?!
Por esse motivo que o template não deve conter lógica de negócio,
apenas lógica que altere a apresentação dos dados!
Agora, nada melhor para aprender sobre a DTL do que botando a mão na
massa e melhorando as páginas da nossa aplicação. E para deixar as páginas
visualmente agradáveis, vamos utilizar o famoso Bootstrap!
Faça isso para todos as bibliotecas externas que queira utilizar (ou utilize
um CDN - Content Delivery Network).
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white">
<a class="navbar-brand" href="{% url 'website:index' %}">
<img src="{% static 'website/img/favicon.png' %}" height='45px'>
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#conteudo-navbar" aria-controls="conteudo-navbar"
aria-expanded="false" aria-label="Ativar navegação">
<span class="navbar-toggler-icon"></span>
</button>
E vamos as explicações:
PÁGINA INICIAL
Template: website/index.html
Para isso, utilizamos a seguinte tag do Django, que serve para que
um template estenda de outro:
{% extends “caminho/para/template” %}
Nesse template:
Top, hein?!
Template: website/cadastra-funcionario.html
Mas antes de seguir, vamos instalar uma biblioteca que vai nos auxiliar e
muito a renderizar os campos de input do nosso formulário: a Widget Tweaks!
Com ela, nós temos maior liberdade para customizar os campos de input
do nosso formulário (adicionando classes CSS e/ou atributos, por exemplo).
INSTALLED_APPS = [
...
'widget_tweaks'
]
{% extends "website/_layouts/base.html" %}
{% load widget_tweaks %}
{% block conteudo %}
<div class="container">
<div class="row">
<div
class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">Cadastro de Funcionário</h5>
<p class="card-text">
Complete o formulário abaixo para cadastrar
um novo <code>Funcionário</code>.
</p>
<form method="post">
<!-- Não se esqueça dessa tag -->
{% csrf_token %}
class InsereFuncionarioForm(forms.ModelForm):
nome = forms.CharField(
max_length=255,
widget=forms.TextInput(
attrs={
'class': "form-control"
}
)
)
...
Mas essa é uma péssima ideia porque bagunça código CSS dentro de
código Python. Não faça isso!
Template: website/lista.html
{% extends "website/_layouts/base.html" %}
{% block conteudo %}
<div class="container">
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">Lista de Funcionário</h5>
{% if funcionarios|length > 0 %}
<p class="card-text">
Aqui está a lista de <code>Funcionários</code>
cadastrados.
</p>
<table class="table">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Nome</th>
<th>Sobrenome</th>
<th>Tempo de Serviço</th>
<th>Remuneração</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for f in funcionarios %}
<tr>
<td>{{ f.id }}</td>
<td>{{ f.nome }}</td>
<td>{{ f.sobrenome }}</td>
<td>{{ f.tempo_de_servico }}</td>
<td>{{ f.remuneracao }}</td>
<td>
<a href="{% url 'website:atualiza_funcionario' pk=f.id %}"
class="btn btn-info">
Atualizar
</a>
<a href="{% url 'website:deleta_funcionario' pk=f.id %}"
class="btn btn-outline-danger">
Excluir
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="text-center mt-5 mb-5 jumbotron">
<h5>Nenhum <code>Funcionário</code> cadastrado ainda.</h5>
</div>
{% endif %}
<hr />
<div class="text-right">
<a class="btn btn-primary"
href="{% url 'website:cadastra_funcionario' %}">
Cadastrar Funcionário
</a>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
Nesse template:
Template: website/atualiza.html
{% extends "website/_layouts/base.html" %}
{% load widget_tweaks %}
{% block conteudo %}
<div class="container">
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">
Atualização de Dados do Funcionário
</h5>
<form method="post">
<!-- Não se esqueça dessa tag -->
{% csrf_token %}
Template: website/exclui.html
Você com certeza aprendeu bastante nessa caminhada! Mas calma que
ainda não terminou, ainda temos mais conteúdo para que você fique craque
em Django! Agora vamos ver como construir tags e filtros customizados!
CONFIGURAÇÃO
- website/
...
- templatetags/
- __init__.py
- tempo_atual.py
- primeira_letra.py
...
Para que o Django enxergue nossas tags e filtros é necessário que o app
onde eles estão instalados esteja configurada na lista INSTALLED_APPS do
settings.py (no nosso caso, website já está lá, portanto, nada a fazer aqui).
FILTRO primeira_letra
● O valor do input.
● O valor do argumento - que pode ter um valor padrão ou não receber
nenhum valor.
Para ser um filtro válido, é necessário que o código dele contenha uma
variável chamada register que seja uma instância de template.Library (onde
todos os tags e filtros são registrados).
Isso define um filtro!
Por isso, nosso filtro deve evitar lançar exceções e, ao invés disso, deve
retornar um valor padrão.
@register.filter(is_safe=True)
@stringfilter
def lower(value):
"""Convert a string into all lowercase."""
return value.lower()
Nele:
Agora que viu um filtro real do Django, vamos codificar e registrar nosso
próprio filtro!
register = template.Library()
@register.filter
@stringfilter
def primeira_letra(value):
return value[0]
Nesse código:
import datetime
from django import template
register = template.Library()
@register.simple_tag
def tempo_atual():
return datetime.datetime.now().strftime('%H:%M:%S')
Agora vamos dar uma olhada nos filtros que estão presentes no próprio
Django: os Built-in Filters!
FILTROS DO DJANGO
É possível fazer muita coisa com os filtros que já vêm instalados no
próprio Django. Muitas vezes, é melhor você fazer algumas operações no
template do que fazê-las no backend (desde que sejam operações de
apresentação, apenas). Sempre verifique a viabilidade de um ou de outro para
facilitar sua vida!
FILTRO capfirst
{{ valor|capfirst }}
Saída:
Esse é um texto
FILTRO cut
{{ valor|cut:" " }}
Saída:
EsseÉUmTextoDeTestes
FILTRO date
O que faz: Utilizado para formatar datas. Possui uma grande variedade de
configurações (veja aqui).
Exemplo:
Entrada: Objeto datetime.
Utilização:
{{ data|date:'d/m/Y' }}
Saída:
01/07/2018
FILTRO filesizeformat
Exemplo:
Entrada: valor = 123456789
Utilização:
{{ valor|filesizeformat }}
Saída:
117.7 MB
FILTRO floatformat
O que faz: Arredonda números com ponto flutuante com o número de casas
decimais passado por argumento.
Exemplo:
Entrada: valor = 14.25145
Utilização:
{{ valor|floatformat:"2" }}
Saída:
14.25
FILTRO join
O que faz: Junta uma lista utilizando a string passada como argumento como
separador.
Exemplo:
Entrada: valor = ["Marcos", "João", "Luiz"]
Utilização:
{{ valor|join:" - " }}
Saída:
FILTRO length
Exemplo:
Entrada: valor = ['Marcos', 'João']
Utilização:
{% if valor|length > 0 %}
<p>Lista contém valores</p>
{% else %}
<p>Lista vazia</p>
{% endif %}
Saída:
FILTRO lower
Exemplo:
Entrada: valor = PaRaLeLePíPeDo
Utilização:
{{ valor|lower }}
Saída:
paralelepípedo
FILTRO pluralize
O que faz: Retorna um sufixo plural caso o número seja maior que 1.
Exemplo:
Entrada: valor = 12
Utilização:
Saída:
FILTRO upper
{{ valor|upper }}
Saída:
TEXTO DE TESTES
FILTRO wordcount
Saída:
Código
O código completo desenvolvido nesse projeto está disponível no Github
da Python Academy. Clique aqui para acessá-lo e baixá-lo!
Conclusão do Capítulo
Neste capítulo vimos como configurar, customizar e estender templates,
como utilizar os filtros e tags do Django, como criar tags e filtros customizados
e um pouquinho de Bootstrap, para deixar as páginas bonitonas!
E AGORA?
FINALIZAÇÃO
UM ATÉ BREVE…
Te espero lá 😉