Escolar Documentos
Profissional Documentos
Cultura Documentos
Página 1
Página 2
Antonio Melé
BIRMINGHAM - MUMBAI
https://translate.googleusercontent.com/translate_f 1/504
16/12/2021 10:51 Sem título
Página 3
Todos os direitos reservados. Nenhuma parte deste livro pode ser reproduzida, armazenada em uma recuperação
sistema, ou transmitido de qualquer forma ou por qualquer meio, sem o prévio escrito
permissão do editor, exceto no caso de breves citações incorporadas
em artigos ou resenhas críticas.
Todo esforço foi feito na preparação deste livro para garantir a precisão
das informações apresentadas. No entanto, as informações contidas neste livro são
vendido sem garantia, expressa ou implícita. Nem o autor, nem Packt
A editora ou seus revendedores e distribuidores serão responsabilizados por quaisquer danos
causado ou alegadamente causado direta ou indiretamente por este livro.
A Packt Publishing tem se empenhado em fornecer informações de marcas registradas sobre todos os
empresas e produtos mencionados neste livro pelo uso adequado de capitais.
No entanto, a Packt Publishing não pode garantir a precisão dessas informações.
ISBN 978-1-83898-195-2
www.packt.com
Página 4
https://translate.googleusercontent.com/translate_f 2/504
16/12/2021 10:51 Sem título
Página 5
https://translate.googleusercontent.com/translate_f 3/504
16/12/2021 10:51 Sem título
packt.com
Inscreva-se em nossa biblioteca digital online para acesso total a mais de 7.000 livros e vídeos,
bem como ferramentas líderes da indústria para ajudá-lo a planejar seu desenvolvimento pessoal e
Avance na sua carreira. Para obter mais informações, visite nosso website.
Você sabia que a Packt oferece versões em e-book de todos os livros publicados, com PDF
e arquivos ePub disponíveis? Você pode atualizar para a versão do e-book emwww.Packt.com
e, como cliente do livro impresso, você tem direito a um desconto na cópia do e-book. Pegue
em contato conosco em customercare@packtpub.com para mais detalhes.
No www.Packt.com , você também pode ler uma coleção de artigos técnicos gratuitos, inscreva-se
para uma variedade de boletins informativos gratuitos e receba descontos e ofertas exclusivas no Packt
livros e e-books.
Página 6
https://translate.googleusercontent.com/translate_f 4/504
16/12/2021 10:51 Sem título
Contribuidores
Sobre o autor
Antonio Melé é o diretor de tecnologia (CTO) da Nucoro, uma empresa com sede em Londres
empresa fintech que fornece uma plataforma de tecnologia líder para construir riqueza digital
soluções de gestão. Antonio desenvolve projetos Django desde 2006
para clientes em vários setores. Em 2009, ele fundou a Zenx IT, uma empresa de desenvolvimento
empresa especializada na construção de produtos digitais. Ele trabalhou como CTO e
consultor de tecnologia para várias start-ups baseadas em tecnologia e ele conseguiu
equipes de desenvolvimento criando projetos para grandes negócios digitais. Antonio segura um
M.Sc. Doutor em Ciência da Computação pela Universidad Pontificia Comillas. Seu pai inspirou
sua paixão por computadores e programação. Você pode descobrir mais sobre Antonio
no site dele https://antoniomele.es/.
Página 7
Sobre os revisores
Jake Kronika , um engenheiro de software sênior com quase 25 anos de experiência,
trabalho com Python desde 2005 e Django desde 2007. Evoluindo junto com
o espaço de desenvolvimento web, seu conjunto de habilidades abrange HTML5, CSS3 e JavaScript
(ECMAScript 6) no front-end, além de Python, Django, Node.js, PHP, Ruby on Rails,
e muito mais além do lado do servidor.
Jake foi co-autor do Django 2 Web Development Cookbook, Terceira edição , publicado em
Outubro de 2018, e Django 3 Web Development Cookbook, Quarta Edição , publicado em
https://translate.googleusercontent.com/translate_f 5/504
16/12/2021 10:51 Sem título
Março de 2020. Além disso, ele atuou como revisor técnico para vários outros Packt
títulos, incluindo:
“Gostaria de agradecer a minha esposa, Veronica, por tudo o que ela faz para apoiar
Eu. Sem ela, eu seria uma fração da pessoa que sou, e teria
realizei apenas uma pequena porcentagem do que fui capaz.
Além disso, gostaria de agradecer ao meu gerente, Ravi, por sua contínua
suporte e aconselhamento. Sua orientação tem sido crítica em vários pontos de
avanço na minha carreira. "
Página 8
David passa grande parte do seu tempo livre trabalhando em projetos de código aberto e
afiar suas habilidades técnicas. Ele gosta de ensinar, ler livros técnicos e
ouvir uma variedade de podcasts e livros de áudio. Quando ele não está trabalhando, ele gosta
passando um tempo com sua linda esposa, quatro filhos maravilhosos e uma miniatura
dachshund.
https://translate.googleusercontent.com/translate_f 6/504
16/12/2021 10:51 Sem título
Página 9
10
Índice
Prefácio XI
Capítulo 1: Construindo um aplicativo de blog 1
Instalando Django 2
Criação de um ambiente Python isolado 3
Instalando Django com pip 3
Criando seu primeiro projeto 4
Executando o servidor de desenvolvimento 6
Configurações do projeto 8
Projetos e aplicações 9
Criação de um aplicativo 10
Projetando o esquema de dados do blog 10
Ativando o aplicativo 13
Criação e aplicação de migrações 13
Criação de um site de administração para modelos 15
https://translate.googleusercontent.com/translate_f 7/504
16/12/2021 10:51 Sem título
Criação de um superusuário 15
O site de administração do Django 16
Adicionando modelos ao site de administração 17
Personalizando a forma como os modelos são exibidos 19
Trabalhando com QuerySets e gerentes 21
Criação de objetos 21
Atualizando objetos 23
Recuperando objetos 23
Usando o método filter () 23
Usando exclude () 24
Usando order_by () 24
Excluindo objetos 24
Quando QuerySets são avaliados 25
[i]
Página 11
Índice
https://translate.googleusercontent.com/translate_f 8/504
16/12/2021 10:51 Sem título
Consultas de ponderação 89
Pesquisando com semelhança de trigrama 90
Outros motores de busca de texto completo 91
Resumo 91
[ ii ]
Página 12
Índice
[ iii ]
https://translate.googleusercontent.com/translate_f 9/504
16/12/2021 10:51 Sem título
Página 13
Índice
[ iv ]
https://translate.googleusercontent.com/translate_f 10/504
16/12/2021 10:51 Sem título
Página 14
Índice
[v]
Página 15
https://translate.googleusercontent.com/translate_f 11/504
16/12/2021 10:51 Sem título
Índice
[ vi ]
Página 16
Índice
https://translate.googleusercontent.com/translate_f 12/504
16/12/2021 10:51 Sem título
Usando mixins para visualizações baseadas em classe 381
Trabalho com grupos e permissões 383
Restringindo o acesso a visualizações baseadas em classe 385
Gerenciando módulos do curso e seus conteúdos 391
Usando conjuntos de formulários para módulos de curso 391
Adicionar conteúdo aos módulos do curso 396
Gerenciando módulos e seus conteúdos 402
Reordenando módulos e seus conteúdos 407
Usando mixins de django-braces 407
Resumo 411
Capítulo 11: Renderizando e armazenando conteúdo em cache 413
Exibindo cursos 414
Adicionando registro de aluno 419
Criação de uma visualização de registro do aluno 419
Inscrever-se em cursos 422
Acessando o conteúdo do curso 425
Renderizando diferentes tipos de conteúdo 429
Usando a estrutura de cache 432
Back-ends de cache disponíveis 432
Instalando Memcached 433
Configurações de cache 434
Adicionando Memcached ao seu projeto 434
Monitorando Memcached 435
Níveis de cache 436
Usando a API de cache de baixo nível 436
Cache baseado em dados dinâmicos 438
Fragmentos de template de cache 440
Cache de visualizações 441
Usando o cache por site 441
Resumo 442
Capítulo 12: Construindo uma API 443
Construindo uma API RESTful 444
Instalando o framework Django REST 444
Definindo serializadores 445
Noções básicas sobre analisadores e renderizadores 446
Lista de construção e visualizações de detalhes 447
Criação de serializadores aninhados 450
Criação de visualizações de API personalizadas 452
Autenticação de tratamento 453
[ vii ]
Página 17
Índice
[ viii ]
Página 18
Índice
https://translate.googleusercontent.com/translate_f 14/504
16/12/2021 10:51 Sem título
[ ix ]
Página 20
19
Prefácio
Django é um framework Python poderoso que incentiva o desenvolvimento rápido
e design limpo e pragmático, ao mesmo tempo que oferece uma curva de aprendizado relativamente superficial.
Isso o torna atraente para programadores novatos e experientes.
Este livro irá guiá-lo por todo o processo de desenvolvimento da web profissional
aplicativos com Django. O livro não cobre apenas os aspectos mais relevantes do
framework, mas também vai te ensinar como integrar outras tecnologias populares
em seus projetos Django.
https://translate.googleusercontent.com/translate_f 15/504
16/12/2021 10:51 Sem título
O livro irá guiá-lo através da criação de aplicativos do mundo real, resolvendo
problemas comuns e implementação de práticas recomendadas, usando uma abordagem passo a passo
isso é fácil de seguir.
Depois de ler este livro, você terá uma boa compreensão de como o Django funciona
e como construir aplicativos da web práticos e avançados.
[ xi ]
Página 21
Prefácio
O Capítulo 2 , Aprimorando Seu Blog com Recursos Avançados , ensinará como lidar com
formulários e ModelForms, enviar e-mails com Django e integrar terceiros
formulários. Você implementará um sistema de comentários para as postagens do seu blog e permitirá
seus usuários compartilhem postagens por e-mail. O capítulo também irá guiá-lo através do
processo de criação de um sistema de etiquetagem.
Capítulo 3 , Estendendo seu aplicativo de blog , explora como criar um modelo personalizado
tags e filtros. O capítulo também mostrará como usar a estrutura do mapa do site
e crie um feed RSS para suas postagens. Você completará sua inscrição de blog em
construir um mecanismo de pesquisa com os recursos de pesquisa de texto completo do PostgreSQL.
O Capítulo 4 , Construindo um site social , explica como construir um site social. Você irá
use a estrutura de autenticação Django para criar visualizações de contas de usuário. Você irá
também aprender a criar um modelo de perfil de usuário personalizado e construir autenticação social
em seu projeto usando as principais redes sociais.
Capítulo 6 , Rastreando as ações do usuário , mostrará como construir um sistema seguidor para
Comercial. Você completará seu site de favoritos de imagens criando uma atividade de usuário
aplicação de stream. Você aprenderá como otimizar QuerySets e trabalhará
https://translate.googleusercontent.com/translate_f 16/504
16/12/2021 10:51 Sem título
com sinais. Por fim, você integrará o Redis em seu projeto para contar visualizações de imagens.
O Capítulo 7 , Construindo uma loja online , explora como criar uma loja online. Você irá
construa modelos de catálogo e criará um carrinho de compras usando sessões Django.
Você construirá um processador de contexto para o carrinho de compras e aprenderá como
para implementar o envio de notificações assíncronas para usuários usando Celery.
[ xii ]
Página 22
Prefácio
O Capítulo 9 , Ampliando Sua Loja , irá ensiná-lo a criar um sistema de cupons para aplicar
descontos para pedidos. O capítulo também mostrará como adicionar internacionalização
ao seu projeto e como traduzir modelos. Finalmente, você construirá um produto
mecanismo de recomendação usando Redis.
Capítulo 12 , Construindo uma API , explora a construção de uma API RESTful para o seu projeto usando
Framework Django REST.
Capítulo 13 , Construindo um Servidor de Chat , explica como usar Canais Django para criar
um servidor de bate-papo em tempo real para alunos. Você aprenderá como implementar funcionalidades
que dependem de comunicação assíncrona por meio de WebSockets.
[ xiii ]
Página 23
Prefácio
Também temos outros pacotes de código de nosso rico catálogo de livros e vídeos disponíveis
em https://github.com/PacktPublishing/ . Dê uma olhada neles!
Convenções usadas
Existem várias convenções de texto usadas ao longo deste livro.
CodeInText : indica palavras de código no texto, nomes de tabelas de banco de dados, nomes de pastas,
nomes de arquivo, extensões de arquivo, nomes de caminho, URLs fictícios, entrada do usuário e Twitter
alças. Por exemplo: "Edite o arquivo models.py do aplicativo da loja ."
admin.site.register (Post)
[ xiv ]
https://translate.googleusercontent.com/translate_f 18/504
16/12/2021 10:51 Sem título
Página 24
Prefácio
Quando desejamos chamar sua atenção para uma parte específica de um bloco de código,
as linhas ou itens relevantes são definidos em negrito:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.apps.BlogConfig',
]
Negrito : indica um novo termo, uma palavra importante ou palavras que você vê na tela.
Por exemplo: "Preencha o formulário e clique no botão Salvar."
Entrar em contato
O feedback de nossos leitores é sempre bem-vindo.
Feedback geral : se você tiver dúvidas sobre qualquer aspecto deste livro, mencione
o título do livro no assunto da sua mensagem e envie-nos um e-mail para customercare @
packtpub.com .
[ xv ]
https://translate.googleusercontent.com/translate_f 19/504
16/12/2021 10:51 Sem título
Página 25
Prefácio
Errata : embora tenhamos tomado todo o cuidado para garantir a precisão de nosso conteúdo,
erros acontecem. Se você encontrou um erro neste livro, ficaríamos gratos
se você pudesse relatar isso para nós. Visite www.packtpub.com/support/errata,
selecione seu livro, clique no link Formulário de Submissão de Errata e insira os detalhes.
Avaliações
Por favor, deixe um comentário. Depois de ler e usar este livro, por que não deixar um
comentário no site em que você o comprou? Os leitores em potencial podem ver e
use sua opinião imparcial para tomar uma decisão de compra, nós da Packt podemos entender
o que você pensa sobre nosso produto, e nosso autor pode ver seus comentários sobre
livro. Obrigada!
[ xvi ]
Página 26
https://translate.googleusercontent.com/translate_f 20/504
16/12/2021 10:51 Sem título
Neste livro, você aprenderá como construir projetos Django completos que estão prontos
para uso em produção. Se você ainda não instalou o Django, você descobrirá como
faça isso na primeira parte deste capítulo.
Este capítulo cobre como criar um aplicativo de blog simples usando Django. O
o objetivo do capítulo é ajudá-lo a ter uma ideia geral de como a estrutura funciona,
uma compreensão de como os diferentes componentes interagem entre si, e
as habilidades para criar facilmente projetos Django com funcionalidade básica. Você será
guiado pela criação de um projeto completo, mas irei entrar em mais detalhes
sobre isso mais tarde. Os diferentes componentes do framework serão explorados em detalhes
ao longo deste livro.
• Instalando Django
• Criação e configuração de um projeto Django
• Criação de um aplicativo Django
• Projetar modelos e gerar migrações de modelos
• Criação de um site de administração para seus modelos
• Trabalhar com QuerySets e gerentes
[1]
Página 27
https://translate.googleusercontent.com/translate_f 21/504
16/12/2021 10:51 Sem título
• Usando as visualizações baseadas em classe do Django
Instalando Django
Se você já instalou o Django, você pode pular esta seção e pular diretamente
para a seção Criando seu primeiro projeto . Django vem como um pacote Python e, portanto,
pode ser instalado em qualquer ambiente Python. Se você ainda não instalou o Django,
o seguinte é um guia rápido para instalá-lo para desenvolvimento local.
Django 3.0 suporta Python 3.6, 3.7 e 3.8. Nos exemplos deste livro,
vamos usar Python 3.8.2. Se você estiver usando Linux ou macOS, provavelmente
Python instalado. Se você estiver usando o Windows, pode baixar um instalador Python
em https://www.python.org/downloads/windows/ .
Se você não tem certeza se o Python está instalado no seu computador, você pode verificar
isso digitando python no shell. Se você vir algo como o seguinte, então
Python está instalado em seu computador:
Se a sua versão instalada do Python for inferior a 3.6 ou se o Python não estiver instalado
em seu computador, baixe Python 3.8.2 em https://www.python.org/
baixa / instala.
Como você usará Python 3, não é necessário instalar um banco de dados. Este Python
versão vem com um banco de dados SQLite embutido. SQLite é um banco de dados leve que
você pode usar com Django para desenvolvimento. Se você planeja implantar seu aplicativo
em um ambiente de produção, você deve usar um banco de dados completo, como
PostgreSQL, MySQL ou Oracle. Você pode encontrar mais informações sobre como
faça seu banco de dados rodar com Django em https://docs.djangoproject.com/
pt / 3.0 / topics / install / # database-installation .
[2]
Página 28
Capítulo 1
Você pode desativar seu ambiente a qualquer momento com o comando deactivate .
Você pode encontrar mais informações sobre venv em https://docs.python.org/3/
library / venv.html .
Execute o seguinte comando no prompt do shell para instalar o Django com pip :
[3]
Página 29
>>> django.get_version ()
'3.0.4'
Se você obtiver uma saída como 3.0.X , o Django foi instalado com sucesso em seu
máquina.
O Django pode ser instalado de várias outras maneiras. Você pode encontrar um
guia de instalação completo em https: //docs.djangoproject.
com / en / 3.0 / topics / install /.
https://translate.googleusercontent.com/translate_f 23/504
16/12/2021 10:51 Sem título
meu site/
manage.py
meu site/
__init__.py
asgi.py
wsgi.py
settings.py
urls.py
[4]
Página 30
Capítulo 1
• manage.py : Este é um utilitário de linha de comando usado para interagir com seu projeto.
É um invólucro fino em torno da ferramenta django-admin.py . Você não precisa editar
este ficheiro.
• mysite / : este é o diretório do seu projeto, que consiste nos seguintes arquivos:
° urls.py : este é o local onde residem os seus padrões de URL. Cada URL
definido aqui é mapeado para uma visualização.
° wsgi.py : esta é a configuração para executar seu projeto como um Web
Aplicativo Server Gateway Interface ( WSGI ).
https://translate.googleusercontent.com/translate_f 24/504
16/12/2021 10:51 Sem título
Os aplicativos Django contêm um arquivo models.py onde os modelos de dados são definidos.
Cada modelo de dados é mapeado para uma tabela de banco de dados. Para completar a configuração do projeto, você
precisa criar as tabelas associadas aos modelos dos aplicativos listados em
INSTALLED_APPS . Django inclui um sistema de migração que gerencia isso.
cd meusite
[5]
Página 31
https://translate.googleusercontent.com/translate_f 25/504
16/12/2021 10:51 Sem título
Você deve ver algo assim:
[6]
Página 32
Capítulo 1
Agora abra http://127.0.0.1:8000/ em seu navegador. Você deve ver uma página
informando que o projeto está sendo executado com sucesso, conforme mostrado na seguinte captura de tela:
A captura de tela anterior indica que o Django está em execução. Se você der uma olhada em
seu console, você verá a solicitação GET realizada por seu navegador:
Cada solicitação HTTP é registrada no console pelo servidor de desenvolvimento. Qualquer erro
que ocorre durante a execução do servidor de desenvolvimento também aparecerá no console.
Você pode executar o servidor de desenvolvimento Django em um host e porta personalizados ou dizer
Django para carregar um arquivo de configurações específico, da seguinte maneira:
Quando você tem que lidar com vários ambientes que exigem
configurações diferentes, você pode criar um arquivo de configurações diferente para
cada ambiente.
https://translate.googleusercontent.com/translate_f 26/504
16/12/2021 10:51 Sem título
ou uWSGI, ou como um aplicativo ASGI usando um servidor como Uvicorn ou Daphne. Você
pode encontrar mais informações sobre como implantar Django com diferentes servidores web em
https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/.
Capítulo 14 , Going Live , explica como configurar um ambiente de produção para o seu
Projetos Django.
[7]
Página 33
Configurações do projeto
Vamos abrir o arquivo settings.py e dar uma olhada na configuração do projeto.
Existem várias configurações que o Django inclui neste arquivo, mas estas são apenas parte do
todas as configurações do Django disponíveis. Você pode ver todas as configurações e seus valores padrão
em https://docs.djangoproject.com/en/3.0/ref/settings/.
[8]
https://translate.googleusercontent.com/translate_f 27/504
16/12/2021 10:51 Sem título
Página 34
Capítulo 1
Não se preocupe se você não entender muito sobre o que está vendo aqui. Você irá
aprenda as diferentes configurações do Django nos capítulos seguintes.
Projetos e aplicações
Ao longo deste livro, você encontrará os termos projeto e aplicação sobre
e acabou. No Django, um projeto é considerado uma instalação do Django com algumas configurações.
Um aplicativo é um grupo de modelos, visualizações, modelos e URLs. Formulários
interagir com a estrutura para fornecer algumas funcionalidades específicas e pode
ser reutilizado em vários projetos. Você pode pensar em um projeto como o seu site, que
contém vários aplicativos, como um blog, wiki ou fórum, que também podem ser usados
por outros projetos.
[9]
https://translate.googleusercontent.com/translate_f 28/504
16/12/2021 10:51 Sem título
Página 35
Criação de um aplicativo
Agora vamos criar seu primeiro aplicativo Django. Você criará um aplicativo de blog
do princípio. No diretório raiz do projeto, execute o seguinte comando:
blog /
__init__.py
admin.py
apps.py
migrações /
__init__.py
models.py
tests.py
views.py
[ 10 ]
Página 36
https://translate.googleusercontent.com/translate_f 29/504
16/12/2021 10:51 Sem título
Capítulo 1
Primeiro, você precisa definir um modelo Post . Adicione as seguintes linhas ao models.py
arquivo do aplicativo do blog :
classe Meta:
ordenação = ('-publicar',)
Este é o seu modelo de dados para postagens de blog. Vamos dar uma olhada nos campos que você acabou de definir
para este modelo:
• título : este é o campo para o título da postagem. Este campo é CharField , que
se traduz em uma coluna VARCHAR no banco de dados SQL.
• slug : Este é um campo que deve ser usado em URLs. Uma lesma é um rótulo curto
que contém apenas letras, números, sublinhados ou hifens. Você vai usar
o campo slug para construir URLs bonitos e amigáveis ao SEO para as postagens do seu blog.
Você adicionou o parâmetro unique_for_date a este campo para que você
pode construir URLs para postagens usando sua data de publicação e slug . Django vai
evitar que várias postagens tenham o mesmo intervalo para uma determinada data.
[ 11 ]
Página 37
• autor : Este campo define uma relação muitos-para-um, o que significa que cada
a postagem é escrita por um usuário, e um usuário pode escrever qualquer número de postagens. Por esta
https://translate.googleusercontent.com/translate_f 30/504
16/12/2021 10:51 Sem título
campo, Django irá criar uma chave estrangeira no banco de dados usando o primário
chave do modelo relacionado. Neste caso, você está contando com o modelo de usuário de
o sistema de autenticação Django. O parâmetro on_delete especifica o
comportamento a ser adotado quando o objeto referenciado é excluído. Isso não é específico
para Django; é um padrão SQL. Usando CASCADE , você especifica que quando
o usuário referenciado é excluído, o banco de dados também excluirá todos os blogs relacionados
Postagens. Você pode dar uma olhada em todas as opções possíveis emhttps: // docs.
djangoproject.com/en/3.0/ref/models/fields/#django.db.models.
ForeignKey.on_delete. Você especifica o nome do relacionamento reverso,
de usuário para postagem , com o atributo related_name . Isso permitirá que você
acesse objetos relacionados facilmente. Você aprenderá mais sobre isso mais tarde.
• corpo : este é o corpo da postagem. Este campo é um campo de texto que traduz
em uma coluna TEXT no banco de dados SQL.
• publicar : esta data e hora indica quando a postagem foi publicada. Você
use o método timezone now do Django como o valor padrão. Isso retorna o
data e hora atual em um formato que reconhece o fuso horário. Você pode pensar nisso como um
versão com reconhecimento de fuso horário do método Python datetime.now padrão .
• criado : esta data e hora indica quando a postagem foi criada. Desde que você
estão usando auto_now_add aqui, a data será salva automaticamente quando
criando um objeto.
• atualizado : esta data e hora indica a última vez que a postagem foi atualizada. Desde a
você está usando auto_now aqui, a data será atualizada automaticamente quando
salvar um objeto.
• status : este campo mostra o status de uma postagem. Você usa um parâmetro de escolhas ,
portanto, o valor deste campo só pode ser definido para uma das opções fornecidas.
Django vem com diferentes tipos de campos que você pode usar para definir seus modelos.
Você pode encontrar todos os tipos de campo em https://docs.djangoproject.com/en/3.0/ref/
modelos / campos /.
A classe Meta dentro do modelo contém metadados. Você diz ao Django para classificar os resultados
pelo campo de publicação em ordem decrescente por padrão quando você consulta o banco de dados.
Você especifica a ordem decrescente usando o prefixo negativo. Fazendo isso, as postagens
publicado recentemente aparecerá primeiro.
[ 12 ]
Página 38
Capítulo 1
Ativando o aplicativo
Para que o Django acompanhe sua aplicação e seja capaz de criar um banco de dados
tabelas para seus modelos, você tem que ativá-lo. Para fazer isso, edite o arquivo settings.py e
https://translate.googleusercontent.com/translate_f 31/504
16/12/2021 10:51 Sem título
adicione blog.apps.BlogConfig à configuração INSTALLED_APPS . Deve ser assim:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.apps.BlogConfig' ,
]
Primeiro, você precisará criar uma migração inicial para seu modelo Post . Na raiz
diretório do seu projeto, execute o seguinte comando:
[ 13 ]
Página 39
Vamos dar uma olhada no código SQL que o Django irá executar no banco de dados para criar
a mesa para o seu modelo. O comando sqlmigrate leva os nomes de migração e
retorna seu SQL sem executá-lo. Execute o seguinte comando para inspecionar o
Saída SQL de sua primeira migração:
COMEÇAR;
https://translate.googleusercontent.com/translate_f 32/504
16/12/2021 10:51 Sem título
-
CREATE TABLE "blog_post" ("id" integer NOT NULL PRIMARY KEY
AUTOINCREMENT, "title" varchar (250) NOT NULL, "slug" varchar (250) NOT
NULL, texto de "corpo" NOT NULL, "publicar" datetime NOT NULL, "criado"
datetime NOT NULL, "atualizado" datetime NOT NULL, "status" varchar (10)
NOT NULL, "author_id" integer NOT NULL REFERÊNCIAS "auth_user" ("id")
DEFERRÁVEL INICIALMENTE DIFERIDO);
COMPROMETER-SE;
A saída exata depende do banco de dados que você está usando. A saída anterior
é gerado para SQLite. Como você pode ver na saída, o Django gera a tabela
nomes combinando o nome do aplicativo e o nome em minúsculas do modelo
( blog_post ), mas você também pode especificar um nome de banco de dados personalizado para o seu modelo em
a classe Meta do modelo usando o atributo db_table .
Django cria uma chave primária automaticamente para cada modelo, mas você também pode
substitua isso especificando primary_key = True em um dos campos do seu modelo.
A chave primária padrão é uma coluna id , que consiste em um inteiro que é
incrementado automaticamente. Esta coluna corresponde ao campo id que é
adicionado automaticamente aos seus modelos.
Vamos sincronizar seu banco de dados com o novo modelo. Execute o seguinte comando para
aplicar migrações existentes:
[ 14 ]
Página 40
Capítulo 1
Se você editar o arquivo models.py para adicionar, remover ou alterar os campos existentes
modelos, ou se você adicionar novos modelos, você terá que criar uma nova migração usando o
comando makemigrations . A migração permitirá que o Django acompanhe o modelo
alterar. Então, você terá que aplicá-lo com o comando migrate para manter o
banco de dados em sincronia com seus modelos.
https://translate.googleusercontent.com/translate_f 33/504
16/12/2021 10:51 Sem título
Criação de um superusuário
Primeiro, você precisará criar um usuário para gerenciar o site de administração. Execute o
seguinte comando:
Senha: ********
[ 15 ]
Página 41
Faça login usando as credenciais do usuário que você criou na etapa anterior. Você irá
consulte a página de índice do site de administração, conforme mostrado na seguinte captura de tela:
https://translate.googleusercontent.com/translate_f 34/504
16/12/2021 10:51 Sem título
[ 16 ]
Página 42
Capítulo 1
Os modelos de grupo e usuário que você pode ver na imagem anterior fazem parte
do framework de autenticação Django localizado em django.contrib.auth . Se vocês
clique em Usuários , você verá o usuário que você criou anteriormente.
admin.site.register (Post)
Agora recarregue o site de administração em seu navegador. Você deve ver a sua postagem
modelo no site, da seguinte forma:
Figura 1.5: O modelo Post do aplicativo de blog incluído na página de índice do site de administração do Django
Isso foi fácil, certo? Quando você registra um modelo no site de administração do Django,
você obtém uma interface amigável gerada pela introspecção de seus modelos que permite
você pode listar, editar, criar e excluir objetos de uma forma simples.
https://translate.googleusercontent.com/translate_f 35/504
16/12/2021 10:51 Sem título
[ 17 ]
Página 43
Clique no link Adicionar ao lado de Postagens para adicionar uma nova postagem. Você notará o formulário que
Django gerou dinamicamente para o seu modelo, conforme mostrado a seguir
captura de tela:
Figura 1.6: O formulário de edição do site de administração do Django para o modelo Post
Django usa widgets de formulário diferentes para cada tipo de campo. Mesmo campos complexos,
como o DateTimeField , são exibidos com uma interface fácil, como um
Selecionador de data JavaScript.
Preencha o formulário e clique no botão SALVAR . Você deve ser redirecionado para a postagem
página de lista com uma mensagem de sucesso e a postagem que você acabou de criar, conforme mostrado no
seguinte captura de tela:
[ 18 ]
https://translate.googleusercontent.com/translate_f 36/504
16/12/2021 10:51 Sem título
Página 44
Capítulo 1
Figura 1.7: A visualização da lista do site de administração do Django para o modelo Post com uma mensagem adicionada com sucesso
@ admin.register (Postagem)
classe PostAdmin (admin.ModelAdmin):
list_display = ('título', 'slug', 'autor', 'publicar', 'status')
Você está informando ao site de administração do Django que seu modelo está registrado em
o site usando uma classe personalizada que herda de ModelAdmin . Nesta aula, você
pode incluir informações sobre como exibir o modelo no site e como
interagir com ele.
O atributo list_display permite que você defina os campos do seu modelo que você
deseja exibir na página de lista de objetos de administração. O @ admin.register ()
decorador executa a mesma função que o admin.site.register () função
que você substituiu, registrando a classe ModelAdmin que ela decora.
[ 19 ]
https://translate.googleusercontent.com/translate_f 37/504
16/12/2021 10:51 Sem título
Página 45
Vamos personalizar o modelo de administrador com mais algumas opções, usando o seguinte código:
@ admin.register (Postagem)
classe PostAdmin (admin.ModelAdmin):
list_display = ('título', 'slug', 'autor', 'publicar', 'status')
list_filter = ('status', 'criado', 'publicar', 'autor')
search_fields = ('título', 'corpo')
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ('autor',)
date_hierarchy = 'publicar'
ordenação = ('status', 'publicar')
Retorne ao seu navegador e recarregue a página da lista de postagens. Agora, ficará assim:
Figura 1.8: A visualização de lista personalizada do site de administração Django para o modelo Post
Você pode ver que os campos exibidos na página da lista de postagens são os que você especificou
no atributo list_display . A página da lista agora inclui uma barra lateral direita que
permite filtrar os resultados pelos campos incluídos no atributo list_filter .
Uma barra de pesquisa apareceu na página. Isso ocorre porque você definiu uma lista
de campos pesquisáveis usando o atributo search_fields . Logo abaixo da pesquisa
barra, existem links de navegação para navegar por uma hierarquia de datas; Este tem
foi definido pelo atributo date_hierarchy . Você também pode ver que as postagens
são ordenados por colunas STATUS e PUBLISH por padrão. Você especificou
os critérios de classificação padrão usando o atributo de ordem .
[ 20 ]
Página 46
https://translate.googleusercontent.com/translate_f 38/504
16/12/2021 10:51 Sem título
Capítulo 1
Em seguida, clique no link ADICIONAR POST . Você também notará algumas mudanças aqui. Como
você digita o título de uma nova postagem, o campo slug é preenchido automaticamente. Você tem
disse ao Django para preencher previamente o campo slug com a entrada do campo de título usando
o atributo prepopulated_fields .
Além disso, o campo do autor agora é exibido com um widget de pesquisa que pode escalar muito
melhor do que uma entrada de seleção suspensa quando você tem milhares de usuários. Isto é
alcançado com o atributo raw_id_fields e se parece com isto:
Figura 1.9: O widget para selecionar objetos relacionados para o campo de autor do modelo Post
Com algumas linhas de código, você personalizou a forma como seu modelo é exibido
no site de administração. Existem muitas maneiras de personalizar e estender o
Site de administração do Django; você aprenderá mais sobre isso posteriormente neste livro.
Depois de criar seus modelos de dados, o Django oferece uma API gratuita para interagir
com eles. Você pode encontrar a referência do modelo de dados da documentação oficial em
https://docs.djangoproject.com/en/3.0/ref/models/.
Criação de objetos
Abra o terminal e execute o seguinte comando para abrir o shell Python:
[ 21 ]
Página 47
https://translate.googleusercontent.com/translate_f 39/504
16/12/2021 10:51 Sem título
>>> de blog.models import Post
>>> user = User.objects.get (username = 'admin')
>>> post = Post (title = 'Outro post',
... slug = 'outro-post',
... body = 'Post body.',
... autor = usuário)
>>> post.save ()
Vamos analisar o que esse código faz. Primeiro, você recupera o objeto do usuário com o
nome de usuário admin :
O método get () permite que você recupere um único objeto do banco de dados. Observação
que este método espera um resultado que corresponda à consulta. Se nenhum resultado for retornado
pelo banco de dados, este método irá gerar uma exceção DoesNotExist , e se o
banco de dados retorna mais de um resultado, ele irá gerar um MultipleObjectsReturned
exceção. Ambas as exceções são atributos da classe do modelo que a consulta está sendo
realizada em.
Em seguida, você cria uma instância de Post com um título, slug e corpo personalizados e define o usuário
que você recuperou anteriormente como autor da postagem:
Finalmente, você salva o objeto Post no banco de dados usando o método save () :
post.save ()
[ 22 ]
Página 48
Capítulo 1
Atualizando objetos
Agora, mude o título da postagem para algo diferente e salve o objeto novamente:
>>> post.save ()
https://translate.googleusercontent.com/translate_f 40/504
16/12/2021 10:51 Sem título
As mudanças que você faz no objeto não são persistentes no
banco de dados até você chamar o método save ().
Recuperando objetos
Você já sabe como recuperar um único objeto do banco de dados usando get ()
método. Você acessou este método usando Post.objects.get () . Cada modelo Django
tem pelo menos um gerenciador, e o gerenciador padrão é chamado de objetos . Você ganha um
Objeto QuerySet usando seu gerenciador de modelos. Para recuperar todos os objetos de uma mesa,
você apenas usa o método all () no gerenciador de objetos padrão, como este:
É assim que você cria um QuerySet que retorna todos os objetos no banco de dados. Observe que
este QuerySet ainda não foi executado. Django QuerySets são preguiçosos , o que significa
eles só são avaliados quando são forçados a sê-lo. Este comportamento torna QuerySets
muito eficiente. Se você não definir o QuerySet como uma variável, mas em vez disso, escreva-o diretamente
no shell Python, a instrução SQL do QuerySet é executada porque você força
para produzir resultados:
>>> all_posts
Você também pode filtrar por vários campos. Por exemplo, você pode recuperar todas as postagens
publicado em 2020 pelo autor com o nome de usuário admin :
[ 23 ]
Página 49
Usando exclude ()
Você pode excluir certos resultados de seu QuerySet usando o método exclude ()
do gerente. Por exemplo, você pode recuperar todas as postagens publicadas em 2020 cujo
os títulos não começam com Por quê :
https://translate.googleusercontent.com/translate_f 41/504
16/12/2021 10:51 Sem título
Usando order_by ()
Você pode ordenar os resultados por campos diferentes usando o método order_by () do
Gerente. Por exemplo, você pode recuperar todos os objetos ordenados por seus títulos , da seguinte maneira:
A ordem crescente está implícita. Você pode indicar a ordem decrescente com um sinal negativo
prefixo, como este:
Excluindo objetos
Se você deseja excluir um objeto, pode fazê-lo a partir da instância do objeto usando o
método delete () :
>>> post.delete ()
[ 24 ]
Página 50
Capítulo 1
https://translate.googleusercontent.com/translate_f 42/504
16/12/2021 10:51 Sem título
status publicado .
Existem duas maneiras de adicionar ou personalizar gerentes para seus modelos: você pode
adicione métodos de gerenciamento extras a um gerente existente ou crie um novo gerenciador por
modificando o QuerySet inicial que o gerente retorna. O primeiro método fornece
você com uma API QuerySet, como Post.objects.my_manager () , e o último
fornece Post.my_manager.all () . O gerente permitirá que você
recupere postagens usando Post.published.all () .
Edite o arquivo models.py do seu aplicativo de blog para adicionar o gerenciador personalizado:
[ 25 ]
Página 51
O primeiro gerenciador declarado em um modelo se torna o gerenciador padrão. Você pode usar
o meta atributo default_manager_name para especificar um gerenciador padrão diferente.
Se nenhum gerenciador for definido no modelo, Django cria automaticamente os objetos
gerenciador padrão para ele. Se você declarar algum gerente para o seu modelo, mas quiser
para manter o gerenciador de objetos também, você deve adicioná-lo explicitamente ao seu modelo.
No código anterior, você adiciona o gerenciador de objetos padrão e o publicado
gerenciador personalizado para o modelo Post .
Agora você definiu seu gerenciador personalizado e o adicionou ao modelo Post ; vocês
pode usá-lo para realizar consultas. Vamos testar.
Agora, você pode importar o modelo de postagem e recuperar todas as postagens publicadas cujo título
começa com Quem , executando o seguinte QuerySet:
Para obter resultados para este QuerySet, certifique-se de definir o campo publicado para
True no objeto Post cujo título começa com Who .
https://translate.googleusercontent.com/translate_f 43/504
16/12/2021 10:51 Sem título
recebe uma solicitação da web e retorna uma resposta da web. Toda a lógica para retornar o
a resposta desejada vai para dentro da visualização.
Primeiro, você criará suas visualizações de aplicativo e, em seguida, definirá um padrão de URL para
cada visualização e, finalmente, você criará modelos HTML para processar os dados gerados
pelas visualizações. Cada visão irá renderizar um template, passando variáveis para ele, e irá
retornar uma resposta HTTP com a saída renderizada.
[ 26 ]
Página 52
Capítulo 1
Você acabou de criar sua primeira visualização Django. A visão post_list aceita a solicitação
objeto como o único parâmetro. Este parâmetro é exigido por todas as visualizações. Nesta visão,
você recupera todas as postagens com o status publicado usando o gerenciador de publicado
que você criou anteriormente.
Finalmente, você usa o atalho render () fornecido pelo Django para renderizar a lista de postagens
com o modelo fornecido. Esta função leva o objeto de solicitação , o caminho do modelo,
e as variáveis de contexto para renderizar o modelo fornecido. Ele retorna um HttpResponse
objeto com o texto renderizado (normalmente código HTML). O atalho render ()
leva o contexto da solicitação em consideração, então qualquer variável definida pelo contexto do modelo
processadores são acessíveis pelo modelo fornecido. Processadores de contexto de modelo são
apenas chamáveis que definem variáveis no contexto. Você aprenderá como usá-los
no Capítulo 3 , Estendendo seu aplicativo de blog .
Vamos criar uma segunda visualização para exibir uma única postagem. Adicione a seguinte função a
o arquivo views.py :
Esta é a visualização de detalhes da postagem. Esta visualização leva o ano , mês , dia e postagem
argumentos para recuperar uma postagem publicada com o slug e a data fornecidos. Observe que
ao criar o modelo Post , você adicionou o parâmetro unique_for_date
para o campo de lesmas . Isso garante que haverá apenas um post com um slug para um
https://translate.googleusercontent.com/translate_f 44/504
16/12/2021 10:51 Sem título
determinada data e, portanto, você pode recuperar postagens únicas usando a data e o slug. No
visualização de detalhes, você usa o atalho get_object_or_404 () para recuperar o
publicar. Esta função recupera o objeto que corresponde aos parâmetros fornecidos ou um
Exceção HTTP 404 (não encontrado) se nenhum objeto for encontrado. Finalmente, você usa o render ()
atalho para renderizar a postagem recuperada usando um modelo.
[ 27 ]
Página 53
app_name = 'blog'
urlpatterns = [
# visualizações de postagem
caminho ('', views.post_list, name = 'post_list'),
path ('<int: year> / <int: month> / <int: day> / <slug: post> /',
views.post_detail,
name = 'post_detail'),
]
Você usa colchetes angulares para capturar os valores do URL. Qualquer valor especificado em
o padrão de URL como <parameter> é capturado como uma string. Você usa conversores de caminho,
como <int: ano> , para corresponder especificamente e retornar um número inteiro e <slug: post>
para corresponder especificamente a uma lesma. Você pode ver todos os conversores de caminho fornecidos pelo Django
em https://docs.djangoproject.com/en/3.0/topics/http/urls/#path-
conversores .
[ 28 ]
https://translate.googleusercontent.com/translate_f 45/504
16/12/2021 10:51 Sem título
Página 54
Capítulo 1
Se usar path () e conversores não for suficiente para você, você pode usar re_path ()
em vez de definir padrões de URL complexos com expressões regulares Python. Você pode
aprenda mais sobre como definir padrões de URL com expressões regulares em https: // docs.
djangoproject.com/en/3.0/ref/urls/#django.urls.re_path. Se você não tem
trabalhou com expressões regulares antes, você pode querer dar uma olhada no
Regular Expression HOWTO localizado emhttps://docs.python.org/3/howto/regex.
html primeiro.
urlpatterns = [
caminho ('admin /', admin.site.urls),
caminho ('blog /', incluir ('blog.urls', namespace = 'blog')) ,
]
O novo padrão de URL definido com include refere-se aos padrões de URL definidos em
o aplicativo de blog para que sejam incluídos no blog / caminho. Você inclui
esses padrões no blog do namespace . Os namespaces devem ser únicos em
todo o seu projeto. Posteriormente, você irá consultar os URLs do seu blog facilmente usando o
namespace seguido por dois pontos e o nome do URL, por exemplo, blog: post_list
e blog: post_detail . Você pode aprender mais sobre namespaces de URL emhttps: //
docs.djangoproject.com/en/3.0/topics/http/urls/#url-namespaces .
[ 29 ]
https://translate.googleusercontent.com/translate_f 46/504
16/12/2021 10:51 Sem título
Página 55
Vamos adicionar modelos ao seu aplicativo para exibir as postagens de uma maneira amigável.
modelos/
blog /
base.html
publicar/
list.html
detail.html
[ 30 ]
Página 56
https://translate.googleusercontent.com/translate_f 47/504
16/12/2021 10:51 Sem título
Capítulo 1
Django tem uma linguagem de template poderosa que permite que você especifique como os dados são
exibido. É baseado em template tags , variáveis de modelo , e filtros de templates :
Você pode ver todas as tags e filtros de modelo integrados em https: //docs.djangoproject.
com / en / 3.0 / ref / templates / builtins /.
{% load static%}
<! DOCTYPE html>
<html>
<head>
<title> {% block title%} {% endblock%} </title>
<link href = "{% static" css / blog.css "%}" rel = "stylesheet">
</head>
<body>
<div id = "content">
{% block content%}
{% endblock%}
</div>
<div id = "sidebar">
<h2> Meu blog </h2>
<p> Este é meu blog. </p>
</div>
</body>
</html>
{% load static%} diz ao Django para carregar as tags do template estático que são fornecidas
pelo aplicativo django.contrib.staticfiles , que está contido no
Configuração INSTALLED_APPS . Depois de carregá-los, você pode usar o {% static%}
tag de modelo em todo este modelo. Com esta tag de modelo, você pode incluir o
arquivos estáticos, como o arquivo blog.css , que você encontrará no código deste exemplo
sob o diretório estático / do aplicativo de blog . Copie o diretório estático /
do código que vem junto com este capítulo para o mesmo local que o seu
projeto para aplicar os estilos CSS aos modelos. Você pode encontrar o conteúdo do diretório
em https://github.com/PacktPublishing/Django-3-by-Example/tree/master/
Capítulo 01 / meusite / blog / estático .
[ 31 ]
Página 57
Você pode ver que existem duas tags {% block%} . Isso diz ao Django que você deseja
para definir um bloco nessa área. Os modelos que herdam deste modelo podem preencher
https://translate.googleusercontent.com/translate_f 48/504
16/12/2021 10:51 Sem título
os blocos com conteúdo. Você definiu um bloco chamado título e um bloco chamado
conteúdo .
{% block content%}
<h1> Meu blog </h1>
{% para postagem em postagens%}
<h2>
<a href="{{ post.get_absolute_url }}">
{{post.title}}
</a>
</h2>
<p class = "data">
Publicado em {{post.publish}} por {{post.author}}
</p>
{{post.body | truncatewords: 30 | linebreaks}}
{% endfor%}
{% endblock%}
Com a tag de template {% extends%} , você diz ao Django para herdar do blog /
modelo base.html . Então, você preenche os blocos de título e conteúdo da base
modelo com conteúdo. Você itera através das postagens e exibe seu título, data,
autor e corpo, incluindo um link no título para o URL canônico da postagem.
[ 32 ]
Página 58
Capítulo 1
https://translate.googleusercontent.com/translate_f 49/504
16/12/2021 10:51 Sem título
{% block content%}
<h1> {{post.title}} </h1>
<p class = "data">
Publicado em {{post.publish}} por {{post.author}}
</p>
{{post.body | quebras de linha}}
{% endblock%}
Em seguida, você pode retornar ao seu navegador e clicar em um dos títulos das postagens para obter uma
observe a visualização de detalhes da postagem. Você deve ver algo assim:
[ 33 ]
Página 59
Adicionando paginação
Quando você começa a adicionar conteúdo ao seu blog, pode facilmente chegar ao ponto onde
dezenas ou centenas de postagens são armazenadas em seu banco de dados. Em vez de exibir todos os
posts em uma única página, você pode querer dividir a lista de posts em várias páginas.
Isso pode ser feito por meio da paginação. Você pode definir o número de postagens que você
deseja ser exibido por página e recuperar as postagens que correspondem à página
solicitado pelo usuário. Django tem uma classe de paginação embutida que permite que você
gerencie dados paginados facilmente.
https://translate.googleusercontent.com/translate_f 50/504
16/12/2021 10:51 Sem título
de django.core.paginator import Paginator, EmptyPage, \
PageNotAnInteger
[ 34 ]
Página 60
Capítulo 1
4. Se o parâmetro da página não for um número inteiro, você recupera a primeira página de resultados.
Se este parâmetro for um número maior do que a última página de resultados, você recupera
a última página.
5. Você passa o número da página e os objetos recuperados para o modelo.
Agora você tem que criar um modelo para exibir o paginador para que ele possa ser
incluído em qualquer modelo que usa paginação. Nos templates / pasta do blog
aplicativo, crie um novo arquivo com o nome pagination.html . Adicione o seguinte
Código HTML para o arquivo:
https://translate.googleusercontent.com/translate_f 51/504
16/12/2021 10:51 Sem título
O modelo links
próximos de paginação espera
e para exibir um objeto
a página atualPágina para
e o total derenderizar
páginas deo resultados.
anterior e Vamos voltar para
o modelo blog / post / list.html e inclua o modelo pagination.html em
a parte inferior do bloco {% content%} , como segue:
{% block content%}
...
{% incluem "pagination.html" com page = posts%}
{% endblock%}
Como o objeto Página que você está passando para o modelo é chamado de postagens , você inclui
o modelo de paginação no modelo de lista de postagens, passando os parâmetros para renderizar
corretamente. Você pode seguir este método para reutilizar seu modelo de paginação no
visualizações paginadas de diferentes modelos.
[ 35 ]
Página 61
https://translate.googleusercontent.com/translate_f 52/504
16/12/2021 10:51 Sem título
em vez de funções. Uma vez que uma visão é um chamável que pega uma solicitação da web e retorna
uma resposta da web, você também pode definir suas visualizações como métodos de classe. Django fornece
classes de visualização de base para isso. Todos eles herdam da classe View , que lida com
Despacho de método HTTP e outras funcionalidades comuns.
As visualizações baseadas em classe oferecem vantagens sobre as visualizações baseadas em função para alguns casos de uso.
Eles têm os seguintes recursos:
• Organizar o código relacionado aos métodos HTTP, como GET , POST ou PUT , em
métodos separados, em vez de usar ramificação condicional
• Usando herança múltipla para criar classes de visão reutilizáveis (também conhecido como
mixins )
[ 36 ]
Página 62
Capítulo 1
Você pode dar uma olhada em uma introdução às visões baseadas em classe em https: // docs.
djangoproject.com/en/3.0/topics/class-based-views/intro/.
Você mudará sua visão post_list para uma visão baseada em classe para usar o genérico
ListView oferecido pelo Django. Esta visualização básica permite listar objetos de qualquer tipo.
Agora abra o arquivo urls.py do seu aplicativo de blog , comente a postagem anterior_
listar o padrão de URL e adicionar um novo padrão de URL usando a classe PostListView , como
segue:
urlpatterns = [
# visualizações de postagem
# path ('', views.post_list, name = 'post_list'),
caminho ('', views.PostListView.as_view (), name = 'post_list') ,
path ('<int: year> / <int: month> / <int: day> / <slug: post> /',
views.post_detail,
name = 'post_detail'),
https://translate.googleusercontent.com/translate_f 53/504
16/12/2021 10:51 Sem título
]
[ 37 ]
Página 63
Para manter a paginação funcionando, você deve usar o objeto de página correto que é
passado para o modelo. A visão genérica ListView do Django passa pela página selecionada
em uma variável chamada page_obj , então você tem que editar seu modelo post / list.html
de acordo para incluir o paginador usando a variável certa, da seguinte maneira:
Resumo
Neste capítulo, você aprendeu o básico da estrutura da web Django criando
um aplicativo de blog simples. Você projetou os modelos de dados e as migrações aplicadas
ao seu projeto. Você também criou as visualizações, modelos e URLs para o seu blog,
incluindo paginação de objeto.
No próximo capítulo, você descobrirá como aprimorar seu aplicativo de blog com
um sistema de comentários e funcionalidade de marcação, e como permitir que seus usuários
compartilhe postagens por e-mail.
[ 38 ]
https://translate.googleusercontent.com/translate_f 54/504
16/12/2021 10:51 Sem título
Página 64
• Compartilhamento de postagens por e-mail : quando os leitores gostam de um artigo, eles podem querer
compartilhe com outra pessoa. Você implementará a funcionalidade para compartilhar
postagens por e-mail.
• Adicionar comentários a uma postagem : muitas pessoas desejam permitir seu público
para comentar em postagens e criar discussões. Você vai deixar seus leitores adicionarem
comentários nas postagens do seu blog.
• Postagens de marcação : as marcações permitem categorizar o conteúdo de uma forma não hierárquica
maneira, usando palavras-chave simples. Você vai implementar um sistema de etiquetagem,
que é um recurso muito popular para blogs.
• Recomendar postagens semelhantes : Assim que você tiver um método de classificação
em vigor, como um sistema de marcação, você pode usá-lo para fornecer conteúdo
recomendações para seus leitores. Você vai construir um sistema que recomenda
outras postagens que compartilham tags com uma determinada postagem do blog.
[ 39 ]
https://translate.googleusercontent.com/translate_f 55/504
16/12/2021 10:51 Sem título
Página 65
• Crie um formulário para os usuários preencherem seu nome, seu e-mail, o destinatário do e-mail,
e comentários opcionais
• Crie uma visão no arquivo views.py que lida com os dados postados e envia
o e-mail
• Adicione um padrão de URL para a nova visualização no arquivo urls.py do blog
aplicativo
• Crie um modelo para exibir o formulário
Primeiro, crie um arquivo forms.py dentro do diretório do seu aplicativo de blog e faça
parece assim:
[ 40 ]
Página 66
https://translate.googleusercontent.com/translate_f 56/504
16/12/2021 10:51 Sem título
Capítulo 2
Este é o seu primeiro formulário Django. Dê uma olhada no código. Você criou um formulário
herdando a classe base do Form . Você usa diferentes tipos de campo para o Django validar
campos em conformidade.
A validação de campo também depende do tipo de campo. Por exemplo, o e- mail e para
campos são campos EmailField . Ambos os campos requerem um endereço de e-mail válido; o campo
caso contrário, a validação gerará uma exceção forms.ValidationError e o formulário
não vai validar. Outros parâmetros também são levados em consideração para a validação do formulário:
você define um comprimento máximo de 25 caracteres para o campo de nome e torna o
campo de comentários opcional com obrigatório = False . Tudo isso também é levado em consideração
para validação de campo. Os tipos de campo usados neste formulário são apenas uma parte do Django
campos do formulário. Para obter uma lista de todos os campos de formulário disponíveis, você pode visitar https: // docs.
djangoproject.com/en/3.0/ref/forms/fields/ .
[ 41 ]
Página 67
if request.method == 'POST':
# Formulário foi enviado
https://translate.googleusercontent.com/translate_f 57/504
16/12/2021 10:51 Sem título
form = EmailPostForm (request.POST)
if form.is_valid ():
# Campos do formulário passaram na validação
cd = form.cleaned_data
# ... enviar email
senão:
form = EmailPostForm ()
return render (request, 'blog / post / share.html', {'post': post,
'formulário': formulário})
1. Quando a visualização é carregada inicialmente com uma solicitação GET , você cria um novo formulário
instância que será usada para exibir o formulário vazio no modelo:
form = EmailPostForm ()
2. O usuário preenche o formulário e o envia via POST . Então, você cria um formulário
instância usando os dados enviados que estão contidos em request.POST :
if request.method == 'POST':
# Formulário foi enviado
form = EmailPostForm (request.POST)
[ 42 ]
Página 68
Capítulo 2
4. Se o formulário não for válido, você renderiza o formulário no modelo novamente com o
dados enviados. Você exibirá erros de validação no modelo.
5. Se o formulário for válido, você recupera os dados validados acessando o formulário.
clean_data . Este atributo é um dicionário de campos de formulário e seus valores.
https://translate.googleusercontent.com/translate_f 58/504
16/12/2021 10:51 Sem título
Agora, vamos explorar como enviar e-mails usando Django para colocar tudo junto.
Se você não pode usar um servidor SMTP, você pode dizer ao Django para escrever e-mails para o console
adicionando a seguinte configuração ao arquivo settings.py :
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Usando esta configuração, o Django irá enviar todos os emails para o shell. Isso é muito útil
para testar seu aplicativo sem um servidor SMTP.
Se quiser enviar e-mails, mas não tiver um servidor SMTP local, você pode
provavelmente use o servidor SMTP do seu provedor de serviço de e-mail. O seguinte exemplo
a configuração é válida para enviar e-mails por meio de servidores do Gmail usando uma conta do Google:
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'sua_conta@gmail.com'
EMAIL_HOST_PASSWORD = 'sua_senha'
[ 43 ]
Página 69
EMAIL_PORT = 587
EMAIL_USE_TLS = Verdadeiro
Execute o comando shell python manage.py para abrir o shell Python e enviar
um e-mail, como segue:
>>> send_mail ('Django mail', 'Este e-mail foi enviado com Django.', 'your_
account@gmail.com ', [' your_account@gmail.com '], fail_silently = False)
https://translate.googleusercontent.com/translate_f 59/504
16/12/2021 10:51 Sem título
Em alguns casos, você também pode ter que desativar o captcha do Gmail em https: // accounts.
google.com/displayunlockcaptcha para enviar e-mails com Django.
if request.method == 'POST':
# Formulário foi enviado
form = EmailPostForm (request.POST)
if form.is_valid ():
[ 44 ]
Página 70
Capítulo 2
Substitua admin@myblog.com por sua conta de e-mail real se você estiver usando um SMTP
servidor em vez do console EmailBackend .
No código acima, você declara uma variável enviada e a define como True quando a postagem foi
enviei. Você usará essa variável posteriormente no modelo para exibir uma mensagem de sucesso
quando o formulário é enviado com sucesso.
Já que você tem que incluir um link para a postagem no e-mail, você recupera o valor absoluto
caminho da postagem usando seu método get_absolute_url () . Você usa este caminho como um
https://translate.googleusercontent.com/translate_f 60/504
16/12/2021 10:51 Sem título
entrada para request.build_absolute_uri () para construir uma URL completa, incluindo o
Esquema HTTP e nome de host. Você constrói o assunto e o corpo da mensagem do
e-mail utilizando os dados limpos do formulário validado e, por fim, envie o e-mail para o
endereço de e-mail contido no campo para do formulário.
Agora que sua visualização está completa, lembre-se de adicionar um novo padrão de URL para ela. Abra o
arquivo urls.py de seu aplicativo de blog e adicione o padrão de URL post_share , da seguinte maneira:
urlpatterns = [
# ...
caminho ('<int: post_id> / share /',
views.post_share, name = 'post_share'),
]
[ 45 ]
Página 71
{% block content%}
{% se enviado%}
<h1> E-mail enviado com sucesso </h1>
p
"{{post.title}}" foi enviado com sucesso para {{form.cleaned_
data.to}}.
</p>
{% senão %}
<h1> Compartilhe "{{post.title}}" por e-mail </h1>
<form method = "post">
{{form.as_p}}
{% csrf_token%}
<input type = "submit" value = "Send e-mail">
</form>
{% fim se %}
{% endblock%}
Este é o modelo para exibir o formulário ou uma mensagem de sucesso quando for enviado. Como você
notará, você cria o elemento de formulário HTML, indicando que ele deve ser enviado
pelo método POST :
Em seguida, você inclui a instância real do formulário. Você diz ao Django para renderizar seus campos em
Parágrafo HTML <p> elementos com o método as_p . Você também pode renderizar o formulário
como uma lista não ordenada com as_ul ou como uma tabela HTML com as_table . Se você quiser
para renderizar cada campo, você pode iterar pelos campos, {{form.as_p}} como no
seguinte exemplo:
<div>
{{field.errors}}
{{field.label_tag}} {{field}}
</div>
{% endfor%}
[ 46 ]
Página 72
Capítulo 2
p
<a href="{% url "blog:post_share" post.id %}">
Compartilhe esta postagem
</a>
</p>
Lembre-se que você está construindo o URL dinamicamente usando o {% url%} modelo
tag fornecida por Django. Você está usando o namespace chamado blog e o URL
nomeado post_share , e você está passando o ID do post como um parâmetro para construir o
URL absoluta.
Figura 2.2: A página de detalhes da postagem, incluindo um link para compartilhar a postagem
https://translate.googleusercontent.com/translate_f 62/504
16/12/2021 10:51 Sem título
[ 47 ]
Página 73
Clique em Compartilhar esta postagem e você deverá ver a página, incluindo o formulário para compartilhar
esta postagem por e-mail, da seguinte forma:
Os estilos CSS do formulário estão incluídos no código de exemplo em static / css / blog.css
Arquivo. Ao clicar no botão ENVIAR E-MAIL , o formulário é enviado e
validado. Se todos os campos contiverem dados válidos, você receberá uma mensagem de sucesso, da seguinte maneira:
Figura 2.4: Uma mensagem de sucesso para uma postagem compartilhada por e-mail
Se você inserir dados inválidos, o formulário será renderizado novamente, incluindo todos os erros de validação:
[ 48 ]
https://translate.googleusercontent.com/translate_f 63/504
16/12/2021 10:51 Sem título
Página 74
Capítulo 2
Observe que alguns navegadores modernos impedem que você envie um formulário com
campos vazios ou errados. Isso ocorre por causa da validação do formulário feita pelo navegador
com base em tipos de campo e restrições por campo. Neste caso, o formulário não será
enviado e o navegador exibirá uma mensagem de erro para os campos que são
errado.
Seu formulário para compartilhar postagens por e-mail agora está completo. Vamos agora criar um comentário
sistema para o seu blog.
[ 49 ]
https://translate.googleusercontent.com/translate_f 64/504
16/12/2021 10:51 Sem título
Página 75
Construindo um modelo
Primeiro, vamos construir um modelo para armazenar comentários. Abra o arquivo models.py do seu blog
aplicativo e adicione o seguinte código:
classe Meta:
ordenação = ('criado',)
Este é o seu modelo de comentário . Ele contém uma ForeignKey para associar um comentário a um
única postagem. Este relacionamento muitos para um é definido no modelo Comentário porque
cada comentário será feito em uma postagem, e cada postagem pode ter vários comentários.
[ 50 ]
Página 76
https://translate.googleusercontent.com/translate_f 65/504
16/12/2021 10:51 Sem título
Capítulo 2
Você pode aprender mais sobre relacionamentos muitos para um em https: // docs.
djangoproject.com/en/3.0/topics/db/examples/many_to_one/.
Você incluiu um campo booleano ativo que usará para desativar manualmente
comentários inadequados. Você usa o campo criado para classificar os comentários em um
ordem cronológica por padrão.
O novo modelo de comentário que você acabou de criar ainda não está sincronizado com o
base de dados. Execute o seguinte comando para gerar uma nova migração que reflete
a criação do novo modelo:
A migração que você acabou de criar foi aplicada; agora uma tabela blog_comment
existe no banco de dados.
Em seguida, você pode adicionar seu novo modelo ao site de administração para gerenciar
comentários através de uma interface simples. Abra o arquivo admin.py do blog
aplicativo, importe o modelo Comment e adicione a seguinte classe ModelAdmin :
@ admin.register (Comentário)
classe CommentAdmin (admin.ModelAdmin):
list_display = ('nome', 'email', 'postagem', 'criado', 'ativo')
list_filter = ('ativo', 'criado', 'atualizado')
search_fields = ('nome', 'email', 'corpo')
[ 51 ]
Página 77
https://translate.googleusercontent.com/translate_f 66/504
16/12/2021 10:51 Sem título
novo modelo incluído na seção BLOG , conforme mostrado na seguinte captura de tela:
O modelo agora está registrado no site de administração, e você pode gerenciar o comentário
instâncias usando uma interface simples.
Para criar um formulário a partir de um modelo, você só precisa indicar qual modelo usar para construir
o formulário na classe Meta do formulário. Django faz uma introspecção do modelo e constrói o
formulário dinamicamente para você.
Cada tipo de campo de modelo possui um tipo de campo de formulário padrão correspondente. A maneira que
você define os campos do seu modelo são levados em consideração para a validação do formulário. Por padrão,
Django constrói um campo de formulário para cada campo contido no modelo. No entanto, você pode
diga explicitamente ao framework quais campos você deseja incluir em seu formulário usando
uma lista de campos , ou defina quais campos você deseja excluir usando uma lista de exclusão de
Campos. Para o seu formulário CommentForm , você usará apenas o nome , e-mail e corpo
campos, porque esses são os únicos campos que seus usuários poderão preencher.
[ 52 ]
Página 78
Capítulo 2
https://translate.googleusercontent.com/translate_f 67/504
16/12/2021 10:51 Sem título
def post_detail (solicitação, ano, mês, dia, postagem):
post = get_object_or_404 (Post, slug = post,
status = 'publicado',
publicar__ano = ano,
publicar__mês = mês,
publicar__dia = dia)
new_comment = Nenhum
if request.method == 'POST':
# Um comentário foi postado
comment_form = CommentForm (data = request.POST)
if comment_form.is_valid ():
# Crie um objeto de comentário, mas não salve no banco de dados ainda
new_comment = comment_form.save (commit = False)
# Atribuir a postagem atual ao comentário
new_comment.post = post
# Salve o comentário no banco de dados
new_comment.save ()
senão:
comment_form = CommentForm ()
retornar render (pedido,
'blog / post / detail.html',
{'post': post,
'comentários': comentários,
'new_comment': new_comment,
'comment_form': comment_form })
Vamos revisar o que você adicionou à sua visualização. Você usou a visão post_detail
para exibir a postagem e seus comentários. Você adicionou um QuerySet para recuperar todos os ativos
comentários para esta postagem, da seguinte maneira:
[ 53 ]
Página 79
Você constrói este QuerySet, começando pelo objeto de postagem . Em vez de construir um
QuerySet para o modelo Comentário diretamente, você aproveita o objeto de postagem para recuperar o
objetos Comentário relacionados . Você usa o gerenciador para os objetos relacionados que você definiu
como comentários usando o atributo related_name do relacionamento no Comentário
modelo. Você usa a mesma visualização para permitir que seus usuários adicionem um novo comentário. Você inicializa
a variável new_comment definindo-a como None . Você usará esta variável quando um
novo comentário é criado.
Você constrói uma instância de formulário com comment_form = CommentForm () se a visão for
chamado por uma solicitação GET . Se a solicitação for feita via POST , você instancia o formulário
usando os dados enviados e validando-os usando o método is_valid () . Se o formulário
é inválido, você renderiza o modelo com os erros de validação. Se o formulário for válido, você
execute as seguintes ações:
https://translate.googleusercontent.com/translate_f 68/504
16/12/2021 10:51 Sem título
O método save () cria uma instância do modelo ao qual o formulário está vinculado
e salva no banco de dados. Se você chamá-lo usando commit = False , você cria
a instância do modelo, mas não a salve no banco de dados ainda. Isso é útil
quando você deseja modificar o objeto antes de finalmente salvá-lo, que é o que
você fará a seguir.
Ao fazer isso, você especifica que o novo comentário pertence a esta postagem.
3. Finalmente, você salva o novo comentário no banco de dados chamando seu save ()
método:
new_comment.save ()
Sua visualização agora está pronta para exibir e processar novos comentários.
[ 54 ]
Página 80
Capítulo 2
Você usa o filtro de modelo pluralize para exibir um sufixo plural para a palavra
"comentário", dependendo do valor total_comments . Filtros de modelo pegam o valor
https://translate.googleusercontent.com/translate_f 69/504
16/12/2021 10:51 Sem título
da variável à qual eles são aplicados como entrada e retornam um valor calculado. Nós
discutirá os filtros de modelo no Capítulo 3 , Estendendo seu aplicativo de blog .
O filtro do modelo pluralize retorna uma string com a letra "s" se o valor for
diferente de 1 . O texto anterior será processado como 0 comentários , 1 comentário ou
N comentários . Django inclui muitas tags de template e filtros que podem te ajudar
para exibir as informações da maneira que desejar.
[ 55 ]
Página 81
Finalmente, você precisa renderizar o formulário ou exibir uma mensagem de sucesso em vez de quando for
submetido com sucesso. Adicione as seguintes linhas logo abaixo do código anterior:
{% if new_comment%}
<h2> Seu comentário foi adicionado. </h2>
{% senão %}
<h2> Adicione um novo comentário </h2>
<form method = "post">
{{comment_form.as_p}}
{% csrf_token%}
<p> <input type = "submit" value = "Add comment"> </p>
</form>
{% fim se %}
https://translate.googleusercontent.com/translate_f 70/504
16/12/2021 10:51 Sem título
Figura 2.7: A página de detalhes da postagem, incluindo o formulário para adicionar um comentário
[ 56 ]
Página 82
Capítulo 2
Adicione alguns comentários usando o formulário. Eles devem aparecer em sua postagem em
ordem cronológica, como segue:
Se você retornar à visualização de detalhes da postagem, notará que o comentário inativo não é
exibido mais; nem é contado para o número total de comentários. Obrigado
ao campo ativo , você pode desativar comentários inadequados e evitar mostrar
-los em suas postagens.
https://translate.googleusercontent.com/translate_f 71/504
16/12/2021 10:51 Sem título
[ 57 ]
Página 83
INSTALLED_APPS = [
# ...
'blog.apps.BlogConfig',
'taggit',
]
O gerenciador de tags permitirá que você adicione, recupere e remova tags do Post
objetos.
Execute o seguinte comando para criar uma migração para as alterações do seu modelo:
[ 58 ]
https://translate.googleusercontent.com/translate_f 72/504
16/12/2021 10:51 Sem título
Página 84
Capítulo 2
Agora, execute o seguinte comando para criar as tabelas de banco de dados necessárias para django-
modelos taggit e para sincronizar suas mudanças de modelo:
Você verá uma saída indicando que as migrações foram aplicadas, da seguinte maneira:
Seu banco de dados agora está pronto para usar modelos django-taggit .
Vamos explorar como usar o gerenciador de tags . Abra o terminal com o python
comando do shell manage.py e digite o seguinte código. Primeiro, você irá recuperar
uma de suas postagens (aquela com 1 ID):
Em seguida, adicione algumas tags a ele e recupere suas tags para verificar se eram
Adicionado com sucesso:
>>> post.tags.all ()
>>> post.tags.all ()
Isso foi fácil, certo? Execute o comando python manage.py runserver para iniciar o
servidor de desenvolvimento novamente e abra http://127.0.0.1:8000/admin/taggit/tag/
no seu navegador.
[ 59 ]
https://translate.googleusercontent.com/translate_f 73/504
16/12/2021 10:51 Sem título
Página 85
Agora, você precisa editar suas postagens de blog para exibir tags. Abra o blog / postagem / lista.
modelo html e adicione o seguinte código HTML abaixo do título da postagem:
O filtro do modelo de junção funciona da mesma forma que o método Python string join () para
concatenar elementos com a string fornecida. Abra http://127.0.0.1:8000/blog/
no seu navegador. Você deve ser capaz de ver a lista de tags sob cada título de postagem:
[ 60 ]
Página 86
https://translate.googleusercontent.com/translate_f 74/504
16/12/2021 10:51 Sem título
Capítulo 2
Em seguida, você editará a visualização post_list para permitir que os usuários listem todas as postagens marcadas com um
tag específica. Abra o arquivo views.py do seu aplicativo de blog , importe o modelo Tag
formulário django-taggit , e mude a visão post_list para opcionalmente filtrar as postagens por
uma tag, como segue:
if tag_slug:
tag = get_object_or_404 (Tag, slug = tag_slug)
object_list = object_list.filter (tags__in = [tag])
Lembre-se de que QuerySets são preguiçosos. Os QuerySets para recuperar postagens serão apenas
avaliada quando você faz um loop na lista de postagens ao renderizar o modelo.
Finalmente, modifique a função render () na parte inferior da visualização para passar a tag
variável para o modelo. A visualização deve ser semelhante a esta:
[ 61 ]
Página 87
if tag_slug:
https://translate.googleusercontent.com/translate_f 75/504
16/12/2021 10:51 Sem título
tag = get_object_or_404 (Tag, slug = tag_slug)
object_list = object_list.filter (tags__in = [tag])
Adicione o seguinte padrão de URL adicional para listar postagens por tag:
Como você pode ver, os dois padrões apontam para a mesma visão, mas você os está nomeando
de forma diferente. O primeiro padrão irá chamar a visão post_list sem qualquer opcional
parâmetros, enquanto o segundo padrão chamará a visualização com o tag_slug
parâmetro. Você usa um conversor de caminho slug para combinar o parâmetro como uma minúscula
string com letras ou números ASCII, além do hífen e caracteres de sublinhado.
Desde que você está usando o post_list visualizar, editar o blog / post / list.html template
e modifique a paginação para usar o objeto posts :
{% se tag%}
<h2> Postagens marcadas com "{{tag.name}}" </h2>
{% fim se %}
[ 62 ]
Página 88
Capítulo 2
Se um usuário estiver acessando o blog, ele verá a lista de todas as postagens. Se eles filtrarem por postagens
marcados com uma marca específica, eles verão a marca pela qual estão filtrando.
https://translate.googleusercontent.com/translate_f 76/504
16/12/2021 10:51 Sem título
</a>
{% if not forloop.last%}, {% endif%}
{% endfor%}
</p>
No código acima, você percorre todas as tags de uma postagem exibindo um link personalizado
ao URL para filtrar postagens por essa tag. Você construiu o URL com {% url "blog: post_
list_by_tag "tag.slug%} , usando o nome da URL e a tag slug como seu
parâmetro. Você separa as tags por vírgulas.
[ 63 ]
Página 89
Para recuperar postagens semelhantes para uma postagem específica, você precisa executar o
seguintes passos:
https://translate.googleusercontent.com/translate_f 77/504
16/12/2021 10:51 Sem título
a postagem mais recente
6. Limite a consulta ao número de postagens que você deseja recomendar
Essas etapas são traduzidas em um QuerySet complexo que você incluirá em seu
visualização post_detail . nova linha o arquivo views.py do seu aplicativo de blog e adicione o
seguinte importação na parte superior:
[ 64 ]
Página 90
Capítulo 2
1. Você recupera uma lista Python de IDs para as tags da postagem atual. Os valores_
list () QuerySet retorna tuplas com os valores dos campos fornecidos. Você
passe flat = True para obter valores únicos, como [1, 2, 3, ...] em vez disso
de uma tupla, como [(1,), (2,), (3,) ...].
2. Você obtém todas as postagens que contêm qualquer uma dessas tags, excluindo a postagem atual
em si.
3. Você usa a função de agregação Contagem para gerar um campo calculado - same_
tags - que contém o número de tags compartilhadas com todas as tags consultadas.
4. Você ordena o resultado pelo número de tags compartilhadas (ordem decrescente) e
por publicar para exibir as postagens recentes primeiro para as postagens com o mesmo número
de tags compartilhadas. Você corta o resultado para recuperar apenas as quatro primeiras postagens.
https://translate.googleusercontent.com/translate_f 78/504
16/12/2021 10:51 Sem título
'comentários': comentários,
'new_comment': new_comment,
'comment_form': comment_form,
'similar_posts': similar_posts })
Agora, edite o modelo blog / post / detail.html e adicione o seguinte código antes
a lista de comentários de postagem:
[ 65 ]
Página 91
Figura 2.14: A página de detalhes da postagem, incluindo uma lista de postagens semelhantes
Agora você pode recomendar com sucesso postagens semelhantes aos seus usuários. django-
taggit também inclui um gerenciador similar_objects () que você pode usar para recuperar
objetos por tags compartilhadas. Você pode dar uma olhada em todos os gerentes django-taggit em
https://django-taggit.readthedocs.io/en/latest/api.html .
Você também pode adicionar a lista de tags ao seu modelo de detalhes de postagem da mesma forma que
você fez no template blog / post / list.html .
Resumo
Neste capítulo, você aprendeu como trabalhar com formulários e modelos de Django.
Você criou um sistema para compartilhar o conteúdo do seu site por e-mail e criou um comentário
sistema para o seu blog. Você adicionou marcação às postagens do seu blog, integrando um
https://translate.googleusercontent.com/translate_f 79/504
16/12/2021 10:51 Sem título
aplicativo e QuerySets complexos construídos para recuperar objetos por similaridade.
No próximo capítulo, você aprenderá como criar tags e filtros de template personalizados.
Você também criará um sitemap e feed personalizados para as postagens do seu blog e implementará
a funcionalidade de pesquisa de texto completo para suas postagens.
[ 66 ]
Página 92
Estendendo seu
3
Aplicativo de blog
O capítulo anterior abordou os conceitos básicos de formulários e a criação de
um sistema de comentários. Você também aprendeu como enviar e-mails com Django, e você
implementou um sistema de etiquetagem integrando um aplicativo de terceiros com o seu
projeto. Neste capítulo, você vai estender seu aplicativo de blog com alguns outros
recursos populares usados em plataformas de blog. Você também aprenderá sobre outros
componentes e funcionalidades com Django.
• Criação de tags e filtros de modelos personalizados : Você aprenderá como construir seu
próprias tags de template e filtros de template para explorar as capacidades do Django
modelos.
• Adicionando um mapa do site e feed de postagem : Você aprenderá como usar os mapas do site
framework e framework de sindicação que vem com Django.
• Implementação de pesquisa de texto completo com PostgreSQL : a pesquisa é muito popular
recurso para blogs. Você aprenderá como implementar uma pesquisa avançada
motor para o seu aplicativo de blog.
https://translate.googleusercontent.com/translate_f 80/504
16/12/2021 10:51 Sem título
[ 67 ]
Página 93
Django também permite que você crie suas próprias tags de template para realizar ações personalizadas.
Tags de modelo personalizado são muito úteis quando você precisa adicionar uma funcionalidade para
seus modelos que não são cobertos pelo conjunto básico de tags de modelo do Django. Isso poderia
ser uma tag para realizar um QuerySet ou qualquer processamento do lado do servidor que você deseja reutilizar
em modelos. Por exemplo, você pode construir uma tag de modelo para exibir a lista de
últimas postagens publicadas em seu blog. Você pode incluir esta lista na barra lateral do
blog para várias páginas, independentemente da vista.
blog /
__init__.py
models.py
...
templatetags /
__init__.py
blog_tags.py
A maneira como você nomeia o arquivo é importante. Você usará o nome deste módulo para carregar
tags em modelos.
[ 68 ]
https://translate.googleusercontent.com/translate_f 81/504
16/12/2021 10:51 Sem título
Página 94
Capítulo 3
Vamos começar criando uma tag simples para recuperar o total de postagens publicadas no blog.
Edite o arquivo blog_tags.py que você acabou de criar e adicione o seguinte código:
register = template.Library ()
@ register.simple_tag
def total_posts ():
return Post.published.count ()
Você criou uma tag de modelo simples que retorna o número de postagens publicadas
até aqui. Cada módulo que contém tags de modelo precisa definir uma variável chamada
registrar - se para ser uma biblioteca de tags válida. Esta variável é uma instância de template.Library ,
e é usado para registrar suas próprias tags e filtros de modelo.
No código acima, você define uma tag chamada total_posts com uma função Python e
use o decorador @ register.simple_tag para registrar a função como uma tag simples.
Django usará o nome da função como o nome da tag. Se você deseja registrá-lo usando
um nome diferente, você pode fazer isso especificando um atributo de nome , como @register.
simples_tag (nome = 'minha_tag') .
Antes de usar tags de modelo personalizado, você deve torná-los disponíveis para o modelo
usando a tag {% load%} . Como mencionado antes, você precisa usar o nome do
Módulo Python contendo suas tags e filtros de template.
{% load blog_tags%}
{% load static%}
<! DOCTYPE html>
<html>
<head>
<title> {% block title%} {% endblock%} </title>
<link href = "{% static" css / blog.css "%}" rel = "stylesheet">
</head>
<body>
[ 69 ]
https://translate.googleusercontent.com/translate_f 82/504
16/12/2021 10:51 Sem título
Página 95
<div id = "content">
{% block content%}
{% endblock%}
</div>
<div id = "sidebar">
<h2> Meu blog </h2>
<p> Este é meu blog. Eu escrevi {% total_posts%} postagens até agora. </
p>
</div>
</body>
</html>
Você precisará reiniciar o servidor para controlar os novos arquivos adicionados ao projeto.
Pare o servidor de desenvolvimento com Ctrl + C e execute-o novamente usando o seguinte
comando:
O poder das tags de modelo personalizado é que você pode processar quaisquer dados e adicioná-los a qualquer
template independentemente da vista executada. Você pode executar QuerySets ou processar qualquer
dados para exibir os resultados em seus modelos.
Agora, você criará outra tag para exibir as últimas postagens na barra lateral do seu
blog. Desta vez, você usará uma tag de inclusão. Usando uma tag de inclusão, você pode renderizar
um modelo com variáveis de contexto retornadas por sua tag de modelo.
[ 70 ]
Página 96
https://translate.googleusercontent.com/translate_f 83/504
16/12/2021 10:51 Sem título
Capítulo 3
Observe que a função retorna um dicionário de variáveis em vez de um valor simples.
As tags de inclusão devem retornar um dicionário de valores, que é usado como contexto para
renderizar o modelo especificado. O modelo de tag que você acabou de criar permite que você especifique
o número opcional de postagens a serem exibidas como {% show_latest_posts 3%} .
Agora, crie um novo arquivo de modelo em blog / post / e nomeie-o mais recente_posts.
html . Adicione o seguinte código a ele:
<ul>
{% para postagem em latest_posts%}
<li>
<a href="{{ post.get_absolute_url }}"> {{post.title}} </a>
</li>
{% endfor%}
</ul>
No código anterior, você exibe uma lista não ordenada de postagens usando o mais recente_
posta variável retornada por sua tag de modelo. Agora, edite o blog / base.html
template e adicione a nova tag template para exibir as últimas três postagens. A barra lateral
o código deve ser semelhante ao seguinte:
<div id = "sidebar">
<h2> Meu blog </h2>
<p> Este é meu blog. Eu escrevi {% total_posts%} postagens até agora. </p>
<h3> Postagens mais recentes </h3>
{% show_latest_posts 3%}
</div>
Em seguida, volte ao navegador e atualize a página. A barra lateral agora deve parecer
como isso:
[ 71 ]
Página 97
Finalmente, você criará uma tag de modelo simples que retorna um valor. Você vai armazenar o
resultar em uma variável que pode ser reutilizada, em vez de gerá-la diretamente. Você irá
https://translate.googleusercontent.com/translate_f 84/504
16/12/2021 10:51 Sem título
crie uma tag para exibir as postagens mais comentadas.
Edite o arquivo blog_tags.py e adicione a seguinte importação e tag de modelo a ele:
@ register.simple_tag
def get_most_commented_posts (contagem = 5):
return Post.published.annotate (
total_comments = Count ('comentários')
) .order_by ('- total_comments') [: contagem]
Além de Count , Django oferece as funções de agregação Avg , Max , Min e Sum .
Você pode ler mais sobre as funções de agregação em https: //docs.djangoproject.
com / en / 3.0 / topics / db / aggregation / .
No código anterior, você armazena o resultado em uma variável personalizada usando o como
argumento seguido do nome da variável. Para sua tag de modelo, você usa {% get_
most_commented_posts as most_commented_posts%} para armazenar o resultado do
tag template em uma nova variável chamada most_commented_posts . Então, você exibe
as postagens retornadas usando uma lista não ordenada.
Agora abra seu navegador e atualize a página para ver o resultado final. Deve parecer
como o seguinte:
[ 72 ]
Página 98
Capítulo 3
https://translate.googleusercontent.com/translate_f 85/504
16/12/2021 10:51 Sem título
Figura 3.3: A visualização da lista de postagens, incluindo a barra lateral completa com as postagens mais recentes e mais comentadas
Agora você tem uma ideia clara sobre como construir tags de modelo customizado. Você pode ler
mais sobre eles em https://docs.djangoproject.com/en/3.0/howto/custom-
template-tags /.
Você pode encontrar a lista de filtros de template embutidos do Django em https: // docs.
djangoproject.com/en/3.0/ref/templates/builtins/#built-in-filter-
referência.
[ 73 ]
Página 99
Você criará um filtro personalizado para permitir o uso da sintaxe de marcação em seu blog
postagens e, em seguida, converta o conteúdo da postagem em HTML nos modelos. Markdown
é uma sintaxe de formatação de texto simples muito simples de usar e deve ser
convertido em HTML. Você pode escrever postagens usando sintaxe de marcação simples e obter
o conteúdo é automaticamente convertido em código HTML. A sintaxe de markdown de aprendizagem é
muito mais fácil do que aprender HTML. Ao usar o markdown, você pode obter outros
contribuidores experientes para escrever facilmente postagens para o seu blog. Você pode aprender o básico do
formato de redução em https://daringfireball.net/projects/markdown/basics .
Primeiro, instale o módulo de marcação Python via pip usando o seguinte comando:
https://translate.googleusercontent.com/translate_f 86/504
16/12/2021 10:51 Sem título
@ register.filter (name = 'markdown')
def markdown_format (texto):
retornar mark_safe (markdown.markdown (texto))
Você registra os filtros de modelo da mesma forma que as tags de modelo. Para prevenir um nome
conflito entre o nome da sua função e o módulo de redução , você nomeia o seu
função markdown_format e nomeia a marcação de filtro para uso em modelos, como
como {{variável | marcação}} . Django escapa do código HTML gerado por filtros;
caracteres de entidades HTML são substituídos por seus caracteres codificados em HTML. Por
exemplo, <p> é convertido em & lt; p & gt; ( símbolo menor que , caractere p , maior que
símbolo). Você usa a função mark_safe fornecida pelo Django para marcar o resultado
como HTML seguro a ser processado no modelo. Por padrão, o Django não confia em nenhum
O código HTML e o escapará antes de colocá-lo na saída. As únicas exceções são
variáveis marcadas como seguras de escape. Este comportamento impede o Django de
produzindo HTML potencialmente perigoso e permite que você crie exceções para
retornando HTML seguro.
Agora, carregue seu módulo de tags de modelo na lista de postagens e nos modelos de detalhes. Adicione o
seguinte linha no topo do blog / post / list.html e blog / post / detail.html
modelos após a tag {% extends%} :
{% load blog_tags%}
[ 74 ]
Página 100
Capítulo 3
{{post.body | markdown}}
* Um
* Dois
https://translate.googleusercontent.com/translate_f 87/504
16/12/2021 10:51 Sem título
* Três
Abra seu navegador e dê uma olhada em como a postagem é renderizada. Você deveria ver o
seguinte saída:
[ 75 ]
Página 101
Como você pode ver na captura de tela anterior, os filtros de modelos personalizados são muito úteis
para personalizar a formatação. Você pode encontrar mais informações sobre filtros personalizados
em https://docs.djangoproject.com/en/3.0/howto/custom-template-
tags / # writing-custom-template-filters .
SITE_ID = 1
# Definição de aplicativo
INSTALLED_APPS = [
# ...
'django.contrib.sites',
'django.contrib.sitemaps',
]
https://translate.googleusercontent.com/translate_f 88/504
16/12/2021 10:51 Sem título
Agora
o bancoexecute o seguinte comando para criar as tabelas do aplicativo do site Django em
de dados:
Em seguida, crie um novo arquivo dentro do diretório do aplicativo de blog e denomine sitemaps.
py . Abra o arquivo e adicione o seguinte código a ele:
[ 76 ]
Página 102
Capítulo 3
O método items () retorna o QuerySet de objetos a serem incluídos neste mapa do site. Por
padrão, Django chama o método get_absolute_url () em cada objeto para recuperar
seu URL. Lembre-se de que você criou este método no Capítulo 1 , Construindo um Blog
Aplicativo , para recuperar a URL canônica para postagens. Se você deseja especificar o URL
para cada objeto, você pode adicionar um método de localização à sua classe de mapa do site.
O método lastmod recebe cada objeto retornado por items () e retorna o último
hora em que o objeto foi modificado.
Finalmente, você só precisa adicionar o URL do seu sitemap. Edite o arquivo urls.py principal do seu
projete e adicione o mapa do site, da seguinte forma:
sitemaps = {
'posts': PostSitemap,
}
https://translate.googleusercontent.com/translate_f 89/504
16/12/2021 10:51 Sem título
urlpatterns = [
caminho ('admin /', admin.site.urls),
caminho ('blog /', incluir ('blog.urls', namespace = 'blog')),
caminho ('sitemap.xml', sitemap, {'sitemaps': sitemaps},
name = 'django.contrib.sitemaps.views.sitemap')
]
[ 77 ]
Página 103
A URL para cada postagem foi construída chamando seu método get_absolute_url () .
Você pode ver que o domínio usado para construir os URLs é example.com . Este domínio
vem de um objeto Site armazenado no banco de dados. Este objeto padrão foi criado
quando você sincronizou a estrutura do site com seu banco de dados.
[ 78 ]
https://translate.googleusercontent.com/translate_f 90/504
16/12/2021 10:51 Sem título
Página 104
Capítulo 3
Figura 3.5: A visualização da lista de administração do Django para o modelo de site da estrutura do site
Figura 3.6: A visualização de edição de administração do Django para o modelo de site da estrutura do site
Os URLs exibidos em seu feed agora serão construídos usando este nome de host. Em um
ambiente de produção, você terá que usar seu próprio nome de domínio para o
estrutura do site.
[ 79 ]
https://translate.googleusercontent.com/translate_f 91/504
16/12/2021 10:51 Sem título
Página 105
Primeiro, você cria uma subclasse da classe Feed da estrutura de distribuição. O título , link ,
e os atributos de descrição correspondem a <title> , <link> e <description>
Elementos RSS, respectivamente.
Você usa reverse_lazy () para gerar a URL para o atributo link . O reverso ()
método permite que você crie URLs por seus nomes e passe parâmetros opcionais.
Você usou reverse () no Capítulo 1 , Construindo um aplicativo de blog . O reverse_lazy ()
função de utilidade é uma versão avaliada preguiçosamente de reverse () . Ele permite que você use um URL
reversão antes que a configuração da URL do projeto seja carregada.
[ 80 ]
Página 106
https://translate.googleusercontent.com/translate_f 92/504
16/12/2021 10:51 Sem título
Capítulo 3
Agora edite o arquivo blog / urls.py , importe o LatestPostsFeed que você acabou de criar,
e instanciar o feed em um novo padrão de URL:
urlpatterns = [
# ...
caminho ('feed /', LatestPostsFeed (), nome = 'post_feed'),
]
Se você abrir o mesmo URL em um cliente RSS, poderá ver seu feed com um
interface amigável.
A etapa final é adicionar um link de inscrição de feed à barra lateral do blog. Abra o blog /
modelo base.html e adicione a seguinte linha sob o número total de postagens
dentro da div da barra lateral :
p
<a href="{% url "blog:post_feed" %}"> Inscreva-se no meu feed RSS </a>
</p>
[ 81 ]
Página 107
https://translate.googleusercontent.com/translate_f 93/504
16/12/2021 10:51 Sem título
Você pode ler mais sobre a estrutura de feed de distribuição Django em https: // docs.
djangoproject.com/en/3.0/ref/contrib/syndication/.
[ 82 ]
Página 108
Capítulo 3
Instalando PostgreSQL
No momento, você está usando SQLite para o seu projeto de blog . Isso é suficiente para
https://translate.googleusercontent.com/translate_f 94/504
16/12/2021 10:51 Sem título
fins de desenvolvimento. No entanto, para um ambiente de produção, você precisará
um banco de dados mais poderoso, como PostgreSQL, MariaDB, MySQL ou Oracle. Você
mudará seu banco de dados para PostgreSQL para se beneficiar de seus recursos de pesquisa de texto completo.
Você também precisa instalar o adaptador PostgreSQL psycopg2 para Python. Execute o
seguinte comando no shell para instalá-lo:
Vamos criar um usuário para seu banco de dados PostgreSQL. Abra o shell e execute o
seguintes comandos:
su postgres
Será solicitada uma senha para o novo usuário. Digite a senha desejada
e, em seguida, criar o banco de dados do blog e dar a propriedade ao usuário do blog que você acabou de
criado com o seguinte comando:
DATABASES = {
'padrão': {
'ENGINE': 'django.db.backends.postgresql',
'NOME': 'blog',
'USUÁRIO': 'blog',
'SENHA': '*****',
}
[ 83 ]
Página 109
Desde que você trocou o banco de dados, não há postagens armazenadas nele. Preencher seu novo
banco de dados com alguns exemplos de postagens de blog para que você possa realizar pesquisas
o banco de dados.
https://translate.googleusercontent.com/translate_f 95/504
16/12/2021 10:51 Sem título
Pesquisas simples
Edite o arquivo settings.py do seu projeto e adicione django.contrib.postgres ao
Configuração INSTALLED_APPS , como segue:
INSTALLED_APPS = [
# ...
'django.contrib.postgres',
]
Agora você pode pesquisar em um único campo usando a pesquisa QuerySet lookup,
como isso:
Esta consulta usa PostgreSQL para criar um vetor de pesquisa para o campo do corpo e uma pesquisa
consulta do termo django . Os resultados são obtidos combinando a consulta com o
vetor.
[ 84 ]
Página 110
Capítulo 3
Post.objects.annotate (
search = SearchVector ('título', 'corpo'),
) .filter (search = 'django')
https://translate.googleusercontent.com/translate_f 96/504
16/12/2021 10:51 Sem título
query = forms.CharField ()
Você usará o campo de consulta para permitir que os usuários introduzam termos de pesquisa. Edite o views.py
arquivo do aplicativo de blog e adicione o seguinte código a ele:
[ 85 ]
Página 111
A visualização de pesquisa está pronta agora. Você precisa criar um modelo para exibir o formulário e
os resultados quando o usuário realiza uma pesquisa. Crie um novo arquivo dentro do blog / post /
diretório de modelo, nomeie-o search.html e adicione o seguinte código a ele:
{% block content%}
{% se consulta%}
<h1> Postagens contendo "{{query}}" </h1>
<h3>
{% com results.count as total_results%}
Encontrado {{total_results}} resultado {{total_results | pluralize}}
{% endwith%}
</h3>
{% para postagem nos resultados%}
<h4> <a href="{{ post.get_absolute_url }}"> {{post.title}} </a> </
h4>
https://translate.googleusercontent.com/translate_f 97/504
16/12/2021 10:51 Sem título
{{post.body | markdown | truncatewords_html: 5}}
{% vazio %}
<p> Não há resultados para sua consulta. </p>
{% endfor%}
<p> <a href="{% url "blog:post_search" %}"> Pesquise novamente </a> </p>
{% senão %}
<h1> Pesquise por postagens </h1>
<form method = "get">
{{form.as_p}}
<input type = "submit" value = "Search">
</form>
{% fim se %}
{% endblock%}
[ 86 ]
Página 112
Capítulo 3
Por fim, edite o arquivo urls.py do seu aplicativo de blog e adicione o seguinte URL
padronizar:
Insira uma consulta e clique no botão PESQUISAR . Você verá os resultados da pesquisa
consulta, da seguinte forma:
https://translate.googleusercontent.com/translate_f 98/504
16/12/2021 10:51 Sem título
[ 87 ]
Página 113
Django fornece uma classe SearchQuery para traduzir termos em um objeto de consulta de pesquisa.
Por padrão, os termos são passados por algoritmos de lematização, o que ajuda você
para obter melhores correspondências. Você também deseja ordenar os resultados por relevância. PostgreSQL
fornece uma função de classificação que ordena os resultados com base na frequência com que os termos de consulta
aparecem e quão próximos estão.
resultados = Post.published.annotate (
search = SearchVector ('título', 'corpo'),
) .filter (pesquisa = consulta)
No código anterior, você cria um objeto SearchQuery , filtra os resultados por ele e
use SearchRank para ordenar os resultados por relevância.
[ 88 ]
https://translate.googleusercontent.com/translate_f 99/504
16/12/2021 10:51 Sem título
Página 114
Capítulo 3
Consultas de ponderação
Você pode aumentar vetores específicos para que mais peso seja atribuído a eles quando
ordenando os resultados por relevância. Por exemplo, você pode usar isso para dar mais relevância
às postagens que correspondem ao título e não ao conteúdo.
No código anterior, você aplica pesos diferentes aos vetores de pesquisa construídos usando
os campos de título e corpo . Os pesos padrão são D , C , B e A , e eles se referem a
os números 0,1 , 0,2 , 0,4 e 1,0 , respectivamente. Você aplica um peso de 1,0 ao
vetor de pesquisa de título e um peso de 0,4 para o vetor de corpo . As correspondências de título prevalecerão
sobre as correspondências de conteúdo corporal. Você filtra os resultados para exibir apenas aqueles com uma classificação
superior a 0,3 .
[ 89 ]
https://translate.googleusercontent.com/translate_f 100/504
16/12/2021 10:51 Sem título
Página 115
blog psql
Vamos editar sua visão e modificá-la para pesquisar trigramas. Edite o arquivo views.py
de seu aplicativo de blog e adicione a seguinte importação:
resultados = Post.published.annotate (
similaridade = TrigramSimilarity ('título', consulta),
) .filter ( similarity__gt = 0.1 ) .order_by ( '-similarity' )
Agora você tem um poderoso mecanismo de pesquisa integrado ao seu projeto. Você pode encontrar mais
informações sobre pesquisa de texto completo em https://docs.djangoproject.com/en/3.0/
ref / contrib / postgres / search / .
[ 90 ]
Página 116
https://translate.googleusercontent.com/translate_f 101/504
16/12/2021 10:51 Sem título
Capítulo 3
Resumo
Neste capítulo, você aprendeu como criar tags e filtros de template Django personalizados
para fornecer modelos com uma funcionalidade personalizada. Você também criou um mapa do site para
motores de busca para rastrear o seu site e um feed RSS para os usuários se inscreverem no seu
blog. Em seguida, você construiu um mecanismo de pesquisa para o seu blog usando o mecanismo de pesquisa de texto completo
do PostgreSQL.
[ 91 ]
Página 118
117
https://translate.googleusercontent.com/translate_f 102/504
16/12/2021 10:51 Sem título
Neste capítulo, começaremos criando uma funcionalidade para os usuários fazerem login, logout,
editar sua senha e redefinir sua senha. Você aprenderá como criar um personalizado
perfil para seus usuários e você adicionará autenticação social ao seu site.
[ 93 ]
Página 119
• Um sistema de autenticação para os usuários se cadastrarem, fazer login, editar seus perfis,
e alterar ou redefinir sua senha
• Um sistema de acompanhamento para permitir que os usuários sigam uns aos outros no site
https://translate.googleusercontent.com/translate_f 103/504
16/12/2021 10:51 Sem título
• Uma funcionalidade para exibir imagens compartilhadas e implementar um bookmarklet
para os usuários compartilharem imagens de qualquer site
• Um fluxo de atividades que permite aos usuários ver o conteúdo carregado pelo
pessoas que eles seguem
mkdir env
A estrutura inicial do projeto foi criada. Use os seguintes comandos para obter
no diretório do projeto e crie um novo aplicativo chamado conta :
marcadores de cd /
Lembre-se de que você deve adicionar o novo aplicativo ao seu projeto adicionando o
nome do aplicativo para a configuração INSTALLED_APPS no arquivo settings.py . Coloque-o
na lista INSTALLED_APPS antes de qualquer um dos outros aplicativos instalados:
[ 94 ]
Página 120
Capítulo 4
INSTALLED_APPS = [
'account.apps.AccountConfig',
# ...
Execute o próximo comando para sincronizar o banco de dados com os modelos do padrão
aplicativos incluídos na configuração INSTALLED_APPS :
Você verá que todas as migrações iniciais do banco de dados Django são aplicadas. Em seguida, você vai
https://translate.googleusercontent.com/translate_f 104/504
16/12/2021 10:51 Sem título
construir um sistema de autenticação em seu projeto usando a autenticação Django
estrutura.
[ 95 ]
Página 121
Middleware são classes com métodos que são executados globalmente durante a solicitação
ou fase de resposta. Você usará classes de middleware em várias ocasiões ao longo
este livro, e você aprenderá como criar middleware personalizado no Capítulo 14 , Going
Ao vivo .
• Usuário : Um modelo de usuário com campos básicos; os principais campos deste modelo são
nome de usuário , senha , email , first_name , last_name e is_active
• Grupo : um modelo de grupo para categorizar os usuários
• Permissão : Sinalizadores para usuários ou grupos realizarem certas ações
• Obtenha o nome de usuário e a senha postados pelo usuário usando um formulário de login
• Autenticar o usuário em relação aos dados armazenados no banco de dados
• Verifique se o usuário está ativo
https://translate.googleusercontent.com/translate_f 105/504
16/12/2021 10:51 Sem título
Primeiro, você criará um formulário de login. Crie um novo arquivo forms.py em sua conta
diretório do aplicativo e adicione as seguintes linhas a ele:
Este formulário será usado para autenticar usuários no banco de dados. Observe que você usa
o widget PasswordInput para renderizar o elemento HTML de senha . Isso incluirá
type = "password" no HTML para que o navegador o trate como uma entrada de senha.
Edite o arquivo views.py do aplicativo de sua conta e adicione o seguinte código a ele:
[ 96 ]
Página 122
Capítulo 4
Isto é o que a visão básica de login faz: quando a visão user_login é chamada com um
Solicitação GET , você instancia um novo formulário de login com form = LoginForm () para exibir
no modelo. Quando o usuário envia o formulário via POST , você executa o
seguintes ações:
https://translate.googleusercontent.com/translate_f 106/504
16/12/2021 10:51 Sem título
usando o método authenticate () . Este método leva o objeto de solicitação ,
o nome de usuário e os parâmetros de senha e retorna o objeto Usuário se
o usuário foi autenticado com sucesso ou nenhum caso contrário. Se o usuário
não foi autenticado, você retorna um HttpResponse bruto , exibindo o
Mensagem de login inválida .
• Se o usuário foi autenticado com sucesso, você verifica se o usuário é
active acessando o atributo is_active . Este é um atributo do Django
modelo de usuário. Se o usuário não estiver ativo, você retorna um HttpResponse que
exibe a mensagem Conta desativada .
• Se o usuário estiver ativo, você o conecta ao site. Você define o usuário em
a sessão chamando o método login () e retornando o Authenticated
mensagem com sucesso .
[ 97 ]
Página 123
Agora você precisará criar um padrão de URL para esta visualização. Crie um novo arquivo urls.py
no diretório do aplicativo de sua conta e adicione o seguinte código a ele:
urlpatterns = [
# visualizações de postagem
caminho ('login /', views.user_login, nome = 'login'),
]
urlpatterns = [
caminho ('admin /', admin.site.urls),
caminho ('conta /', incluir ('conta.urls')) ,
]
A visualização de login agora pode ser acessada por um URL. É hora de criar um modelo para
esta vista. Como você não tem nenhum modelo para este projeto, pode começar por
criando um modelo básico que pode ser estendido pelo modelo de login. Crie o
seguintes arquivos e diretórios dentro do diretório do aplicativo da conta :
modelos/
conta/
login.html
base.html
{% load static%}
https://translate.googleusercontent.com/translate_f 107/504
16/12/2021 10:51 Sem título
<! DOCTYPE html>
<html>
<head>
<title> {% block title%} {% endblock%} </title>
<link href = "{% static" css / base.css "%}" rel = "stylesheet">
[ 98 ]
Página 124
Capítulo 4
</head>
<body>
<div id = "header">
<span class = "logo"> Favoritos </span>
</div>
<div id = "content">
{% block content%}
{% endblock%}
</div>
</body>
</html>
Este será o template base para o site. Como você fez em seu projeto anterior,
inclua os estilos CSS no template principal. Você pode encontrar esses arquivos estáticos no
código que vem junto com este capítulo. Copie o diretório estático / da conta
aplicação do código-fonte do capítulo para o mesmo local em seu projeto,
que você pode usar os arquivos estáticos. Você pode encontrar o conteúdo do diretório emhttps: //
github.com/PacktPublishing/Django-3-by-Example/tree/master/Chapter04/
marcadores / conta / estático .
O modelo básico define um bloco de título e um bloco de conteúdo que pode ser preenchido
com conteúdo pelos modelos que se estendem a partir dele.
Vamos preencher o modelo para o seu formulário de login. Abra a conta / login.html
modelo e adicione o seguinte código a ele:
{% extends "base.html"%}
{% block content%}
<h1> Login </h1>
<p> Por favor, use o seguinte formulário para fazer o login: </p>
<form method = "post">
{{form.as_p}}
{% csrf_token%}
<p> <input type = "submit" value = "Log in"> </p>
</form>
{% endblock%}
[ 99 ]
https://translate.googleusercontent.com/translate_f 108/504
16/12/2021 10:51 Sem título
Página 125
Ainda não há usuários em seu banco de dados. Você precisará criar um superusuário primeiro
para poder acessar o site de administração para gerenciar outros usuários. Aberto
a linha de comando e execute python manage.py makesuperuser . Preencha o
nome de usuário, e-mail e senha desejados. Em seguida, execute o servidor de desenvolvimento usando
o comando python manage.py runserver e abre http://127.0.0.1:8000/
admin / em seu navegador. Acesse o site de administração usando as credenciais do
usuário que você acabou de criar. Você verá o site de administração do Django, incluindo o
Modelos de usuário e grupo do framework de autenticação Django.
Será o seguinte:
Figura 4.1: A página de índice do site de administração do Django incluindo usuários e grupos
[ 100 ]
https://translate.googleusercontent.com/translate_f 109/504
16/12/2021 10:51 Sem título
Página 126
Capítulo 4
Agora, envie o formulário, deixando um dos campos vazio. Neste caso, você verá que
o formulário não é válido e exibe erros, da seguinte forma:
Observe que alguns navegadores modernos impedem que você envie o formulário
com campos vazios ou errados. Isso ocorre por causa da validação do formulário feita pelo
navegador com base em tipos de campo e restrições por campo. Neste caso, o formulário não
ser enviado e o navegador exibirá uma mensagem de erro para os campos que são
errado.
Se você inserir um usuário inexistente ou uma senha errada, você receberá um login inválido
mensagem.
Se você inserir credenciais válidas, receberá uma mensagem Autenticado com sucesso ,
como isso:
[ 101 ]
Página 127
https://translate.googleusercontent.com/translate_f 110/504
16/12/2021 10:51 Sem título
Django fornece as seguintes visões baseadas em classes para lidar com autenticação.
Todos eles estão localizados em django.contrib.auth.views :
O Django também inclui as seguintes visualizações para permitir que os usuários redefinam suas senhas:
As visualizações listadas nas listas anteriores podem economizar muito tempo ao criar um
site com contas de usuário. As visualizações usam valores padrão que você pode substituir,
como a localização do modelo a ser processado ou o formulário a ser usado por
a vista.
urlpatterns = [
# visualização de login anterior
# caminho ('login /', views.user_login, name = 'login'),
caminho ('login /', auth_views.LoginView.as_view (), nome = 'login') ,
caminho ('logout /', auth_views.LogoutView.as_view (), nome = 'logout') ,
]
[ 102 ]
Página 128
Capítulo 4
https://translate.googleusercontent.com/translate_f 111/504
16/12/2021 10:51 Sem título
estrutura. Você também adiciona um padrão de URL para a visualização LogoutView .
Crie um novo diretório dentro do diretório de modelos do aplicativo de sua conta
e nomeie-o como registro . Este é o caminho padrão onde a autenticação do Django
visualizações esperam que seus modelos de autenticação sejam.
{% extends "base.html"%}
{% block content%}
<h1> Login </h1>
{% if form.errors%}
p
Seu nome de usuário e senha não coincidem.
Por favor, tente novamente.
</p>
{% senão %}
<p> Por favor, use o seguinte formulário para fazer o login: </p>
{% fim se %}
<div class = "login-form">
<form action = "{% url 'login'%}" method = "post">
{{form.as_p}}
{% csrf_token%}
<input type = "hidden" name = "next" value = "{{next}}" />
<p> <input type = "submit" value = "Log-in"> </p>
</form>
</div>
{% endblock%}
Este modelo de login é bastante semelhante ao que você criou antes. Django usa o
Formulário AuthenticationForm localizado em django.contrib.auth.forms por padrão.
Este formulário tenta autenticar o usuário e gera um erro de validação se o login foi
mal sucedido. Neste caso, você pode procurar erros usando {% if form.errors%} em
o modelo para verificar se as credenciais fornecidas estão erradas.
[ 103 ]
Página 129
Observe que você adicionou um elemento HTML oculto <input> para enviar o valor de
uma variável chamada next . Esta variável é primeiro definida pela visualização de login quando você passa um
próximo parâmetro na solicitação (por exemplo, http://127.0.0.1:8000/account/
login /? próximo = / conta / ).
O próximo parâmetro deve ser um URL. Se este parâmetro for fornecido, o login do Django
view redirecionará o usuário para o URL fornecido após um login bem-sucedido.
https://translate.googleusercontent.com/translate_f 112/504
16/12/2021 10:51 Sem título
{% extends "base.html"%}
{% block content%}
<h1> Desconectado </h1>
p
Você foi desconectado com sucesso.
Você pode <a href="{% url "login" %}"> fazer login novamente </a>.
</p>
{% endblock%}
Depois de adicionar os padrões de URL e os modelos para visualizações de login e logout, seu
o site agora está pronto para que os usuários façam login usando as visualizações de autenticação do Django.
Agora, você criará uma nova visualização para exibir um painel quando os usuários fizerem login em seus
conta. Abra o arquivo views.py do aplicativo de sua conta e adicione o seguinte
código para ele:
@login_required
painel def (solicitação):
retornar render (pedido,
'account / dashboard.html',
{'seção': 'painel'})
[ 104 ]
Página 130
Capítulo 4
Ao fazer isso, a visualização de login redireciona os usuários para a URL que eles estavam tentando
acesso depois de fazerem login com sucesso. Lembre-se de que você adicionou uma entrada oculta no
o formulário do seu modelo de login para este propósito.
Você também pode definir uma variável de seção . Você usará esta variável para rastrear o
seção do site que o usuário está navegando. Múltiplas visualizações podem corresponder ao mesmo
seção. Esta é uma maneira simples de definir a seção à qual cada vista corresponde.
Em seguida, você precisará criar um modelo para a visualização do painel. Crie um novo arquivo
dentro do diretório templates / account / e nomeie-o como dashboard.html . Faça
parece com isso:
{% extends "base.html"%}
{% block content%}
https://translate.googleusercontent.com/translate_f 113/504
16/12/2021 10:51 Sem título
<h1> Painel </h1>
<p> Bem-vindo ao seu painel. </p>
{% endblock%}
Em seguida, adicione o seguinte padrão de URL para esta visualização no arquivo urls.py da conta
aplicativo:
urlpatterns = [
# ...
caminho ('', views.dashboard, name = 'painel') ,
]
LOGIN_REDIRECT_URL = 'painel'
LOGIN_URL = 'login'
LOGOUT_URL = 'logout'
• LOGIN_URL : O URL para redirecionar o usuário para fazer login (por exemplo, visualizações usando
o decorador login_required )
• LOGOUT_URL : O URL para redirecionar o usuário para fazer logout
Você está usando os nomes dos padrões de URL que definiu anteriormente usando
o atributo name da função path () . URLs codificados em vez de nomes de URL
também pode ser usado para essas configurações.
[ 105 ]
Página 131
Agora, você adicionará links de login e logout ao seu modelo básico para colocar tudo
juntos. Para fazer isso, você deve determinar se o usuário atual é
conectado ou não, a fim de exibir o link apropriado para cada caso. O actual
o usuário é definido no objeto HttpRequest pelo middleware de autenticação. Você pode
acesse-o com request.user . Você encontrará um objeto Usuário na solicitação, mesmo que
o usuário não está autenticado. Um usuário não autenticado é definido na solicitação como um
instância de AnonymousUser . A melhor maneira de verificar se o usuário atual é
autenticado é acessando o atributo somente leitura is_authenticated .
<div id = "header">
<span class = "logo"> Favoritos </span>
{% if request.user.is_authenticated%}
https://translate.googleusercontent.com/translate_f 114/504
16/12/2021 10:51 Sem título
<ul class = "menu">
<li {% if section == "dashboard"%} class = "selected" {% endif%}>
<a href="{% url "dashboard" %}"> Meu painel </a>
</li>
<li {% if section == "images"%} class = "selected" {% endif%}>
<a href="#"> Imagens </a>
</li>
<li {% if section == "people"%} class = "selected" {% endif%}>
<a href="#"> Pessoas </a>
</li>
</ul>
{% fim se %}
[ 106 ]
Página 132
Capítulo 4
Como você pode ver no código anterior, você só exibe o menu do site para
usuários autenticados. Você também verifica a seção atual para adicionar uma classe selecionada
atributo para o item <li> correspondente , a fim de destacar a seção atual
no menu usando CSS. Você exibe o nome do usuário e um link para fazer logout se o
o usuário é autenticado ou um link para fazer login caso contrário.
Você pode ver que a seção Meu painel é destacada com CSS porque
tem uma classe selecionada . Uma vez que o usuário é autenticado, o primeiro nome do usuário é
exibido no lado direito do cabeçalho. Clique no link Logout . Você deve
veja a seguinte página:
https://translate.googleusercontent.com/translate_f 115/504
16/12/2021 10:51 Sem título
Figura 4.6: A página desconectada
Na página da captura de tela anterior, você pode ver que o usuário está desconectado,
e, portanto, o menu do site não está mais sendo exibido. Agora o
O link no lado direito do cabeçalho mostra Log-in .
[ 107 ]
Página 133
{% extends "base.html"%}
{% block content%}
<h1> Altere sua senha </h1>
<p> Use o formulário abaixo para alterar sua senha. </p>
<form method = "post">
{{form.as_p}}
<p> <input type = "submit" value = "Change"> </p>
{% csrf_token%}
</form>
{% endblock%}
{% extends "base.html"%}
https://translate.googleusercontent.com/translate_f 116/504
16/12/2021 10:51 Sem título
{% block content%}
<h1> Senha alterada </h1>
<p> Sua senha foi alterada com sucesso. </p>
{% endblock%}
[ 108 ]
Página 134
Capítulo 4
Preencha o formulário com sua senha atual e sua nova senha, e clique em
o botão ALTERAR . Você verá a seguinte página de sucesso:
[ 109 ]
https://translate.googleusercontent.com/translate_f 117/504
16/12/2021 10:51 Sem título
Página 135
Saia e faça login novamente usando sua nova senha para verificar se tudo funciona
como esperado.
{% extends "base.html"%}
{% block content%}
<h1> Esqueceu sua senha? </h1>
<p> Digite seu endereço de e-mail para obter uma nova senha. </p>
<form method = "post">
{{form.as_p}}
<p> <input type = "submit" value = "Send e-mail"> </p>
{% csrf_token%}
</form>
{% endblock%}
[ 110 ]
https://translate.googleusercontent.com/translate_f 118/504
16/12/2021 10:51 Sem título
Página 136
Capítulo 4
{% extends "base.html"%}
{% block content%}
<h1> Redefina sua senha </h1>
<p> Enviamos por e-mail instruções para definir sua senha. </p>
<p> Se você não receber um e-mail, certifique-se de ter inserido
o endereço com o qual você se registrou. </p>
{% endblock%}
{% extends "base.html"%}
{% block content%}
<h1> Redefina sua senha </h1>
{% se link válido%}
<p> Insira sua nova senha duas vezes: </p>
<form method = "post">
{{form.as_p}}
{% csrf_token%}
<p> <input type = "submit" value = "Alterar minha senha" /> </p>
</form>
{% senão %}
<p> O link de redefinição de senha era inválido, possivelmente porque
já foi usado. Solicite uma nova redefinição de senha. </p>
{% fim se %}
{% endblock%}
[ 111 ]
Página 137
https://translate.googleusercontent.com/translate_f 119/504
16/12/2021 10:51 Sem título
{% extends "base.html"%}
{% block content%}
<h1> Senha definida </h1>
<p> Sua senha foi definida. Você pode
<a href="{% url "login" %}"> faça login agora </a> </p>
{% endblock%}
<p> <a href="{% url "password_reset" %}"> Esqueceu sua senha? </a> </
p>
Neste ponto, você precisa adicionar um protocolo SMTP ( Simple Mail Transfer Protocol )
configuração para o arquivo settings.py do seu projeto para que o Django seja capaz de
Mande emails. Você aprendeu como adicionar configurações de e-mail ao seu projeto no Capítulo 2 ,
Aprimorando seu blog com recursos avançados . No entanto, durante o desenvolvimento, você
pode configurar o Django para escrever e-mails para a saída padrão em vez de enviar
-los por meio de um servidor SMTP. Django fornece um back-end de e-mail para escrever e-mails
para o console. Edite o arquivo settings.py do seu projeto e adicione a seguinte linha:
[ 112 ]
Página 138
Capítulo 4
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
https://translate.googleusercontent.com/translate_f 120/504
16/12/2021 10:51 Sem título
Dê uma olhada no console onde você está executando o servidor de desenvolvimento. Você
verá o e-mail gerado, da seguinte maneira:
Para: usuário@domínio.com
http://127.0.0.1:8000/account/reset/MQ/45f-9c3f30caafd523055fcc/
[ 113 ]
Página 139
Copie o URL e abra-o em seu navegador. Você deverá ver a seguinte página:
https://translate.googleusercontent.com/translate_f 121/504
16/12/2021 10:51 Sem título
Agora você pode fazer login novamente em sua conta usando sua nova senha.
Cada token para definir uma nova senha pode ser usado apenas uma vez. Se você abrir o link
você recebeu novamente, receberá uma mensagem informando que o token é inválido.
[ 114 ]
Página 140
Capítulo 4
O Django também fornece os padrões de URL de autenticação que você acabou de criar. Você
pode comentar os padrões de URL de autenticação que você adicionou ao urls.py
arquivo do aplicativo de conta e inclua django.contrib.auth.urls em vez disso,
do seguinte modo:
urlpatterns = [
# ...
path ('', include ('django.contrib.auth.urls')) ,
]
https://translate.googleusercontent.com/translate_f 122/504
16/12/2021 10:51 Sem título
Registro de
Os usuários existentes agorausuário elogout,
podem fazer login, perfis desenhausuário
alterar sua e redefinir seu
senha. Agora, você precisará construir uma visão para permitir que os visitantes criem um usuário
conta.
Registro do usuário
Vamos criar uma visualização simples para permitir o registro do usuário em seu site. Inicialmente,
você tem que criar um formulário para permitir que o usuário insira um nome de usuário, seu nome real e um
senha.
classe Meta:
model = User
campos = ('nome de usuário', 'primeiro_nome', 'e-mail')
[ 115 ]
Página 141
cd = self.cleaned_data
se cd ['senha']! = cd ['senha2']:
raise forms.ValidationError ('As senhas não correspondem.')
return cd ['senha2']
Você criou um formulário de modelo para o modelo de usuário. Em seu formulário, você inclui apenas
os campos de nome de usuário , primeiro_nome e e- mail do modelo. Esses campos serão
validado com base em seus campos de modelo correspondentes. Por exemplo, se o usuário escolher
um nome de usuário que já existe, eles obterão um erro de validação porque o nome de usuário é
um campo definido com unique = True .
Você adicionou dois campos adicionais - senha e senha 2 - para os usuários definirem
sua senha e confirme-a. Você definiu um método clean_password2 () para
compare a segunda senha com a primeira e não deixe o formulário validar se o
as senhas não coincidem. Esta verificação é feita quando você valida o formulário chamando
seu método is_valid () . Você pode fornecer um método clean_ <fieldname> () para qualquer
dos campos do formulário para limpar o valor ou gerar erros de validação do formulário para um
campo específico. Os formulários também incluem um método clean () geral para validar todo o
formulário, que é útil para validar campos que dependem uns dos outros. Neste caso, você
use a validação clean_password2 () específica do campo em vez de sobrescrever o
método clean () do formulário. Isso evita substituir outras verificações específicas de campo que
o ModelForm obtém das restrições definidas no modelo (por exemplo, validando
que o nome de usuário é único).
https://translate.googleusercontent.com/translate_f 123/504
16/12/2021 10:51 Sem título
Django
em também fornece um eformulário
django.contrib.auth.forms UserCreationForm
é muito semelhante que você
ao que você criou.pode usar, que reside
[ 116 ]
Página 142
Capítulo 4
senão:
user_form = UserRegistrationForm ()
retornar render (pedido,
'account / register.html',
{'user_form': user_form})
A visão para a criação de contas de usuário é bastante simples. Por razões de segurança, em vez de
salvando a senha crua inserida pelo usuário, você usa o método set_password ()
do modelo de usuário que lida com o hash.
{% extends "base.html"%}
{% block content%}
<h1> Crie uma conta </h1>
<p> Por favor, inscreva-se usando o seguinte formulário: </p>
<form method = "post">
{{user_form.as_p}}
{% csrf_token%}
<p> <input type = "submit" value = "Create my account"> </p>
</form>
{% endblock%}
https://translate.googleusercontent.com/translate_f 124/504
16/12/2021 10:51 Sem título
Adicione o seguinte código a ele:
{% extends "base.html"%}
{% block content%}
<h1> Bem-vindo, {{new_user.first_name}}! </h1>
<p> Sua conta foi criada com sucesso. Agora você pode <a
href = "{% url" login "%}"> faça login </a>. </p>
{% endblock%}
[ 117 ]
Página 143
https://translate.googleusercontent.com/translate_f 125/504
16/12/2021 10:51 Sem título
mensagem:
[ 118 ]
Página 144
Capítulo 4
Clique no link de login e digite seu nome de usuário e senha para verificar se você
pode acessar sua conta.
Você também pode adicionar um link para o registro em seu modelo de login. Edite o
modelo registration / login.html e encontre a seguinte linha:
<p> Por favor, use o seguinte formulário para fazer o login: </p>
<p> Por favor, use o seguinte formulário para fazer o login. Se você não tem um
conta <a href="{% url "register" %}"> registre-se aqui </a> </p>
Edite o arquivo models.py do seu aplicativo de conta e adicione o seguinte código a ele:
[ 119 ]
https://translate.googleusercontent.com/translate_f 126/504
16/12/2021 10:51 Sem título
Página 145
O campo um para um usuário permite que você associe perfis aos usuários. Você usa
CASCADE para o parâmetro on_delete para que seu perfil relacionado também seja excluído
quando um usuário é excluído. O campo da foto é um campo ImageField . Você vai precisar
instale a biblioteca Pillow para lidar com imagens. Instale o travesseiro executando o seguinte
comando em seu shell:
Para habilitar o Django a servir arquivos de mídia enviados por usuários com o desenvolvimento
servidor, adicione as seguintes configurações ao arquivo settings.py do seu projeto:
MEDIA_URL é o URL base usado para servir os arquivos de mídia carregados pelos usuários, e
MEDIA_ROOT é o caminho local onde residem. Você constrói o caminho dinamicamente
em relação ao caminho do projeto para tornar o código mais genérico.
Agora, edite o arquivo urls.py principal do projeto de favoritos e modifique o código, como
segue:
urlpatterns = [
caminho ('admin /', admin.site.urls),
caminho ('conta /', incluir ('conta.urls')),
]
[ 120 ]
https://translate.googleusercontent.com/translate_f 127/504
16/12/2021 10:51 Sem título
Página 146
Capítulo 4
if settings.DEBUG:
urlpatterns + = static (settings.MEDIA_URL,
document_root = settings.MEDIA_ROOT)
Abra o shell e execute o seguinte comando para criar a migração do banco de dados
para o novo modelo:
@ admin.register (perfil)
classe ProfileAdmin (admin.ModelAdmin):
list_display = ['usuário', 'date_of_birth', 'foto']
[ 121 ]
Página 147
https://translate.googleusercontent.com/translate_f 128/504
16/12/2021 10:51 Sem título
Em seguida, você permitirá que os usuários editem seus perfis no site. Adicione a seguinte importação
e formulários de modelo para o arquivo forms.py do aplicativo de conta :
[ 122 ]
Página 148
Capítulo 4
Quando os usuários se registram em seu site, você cria um perfil vazio associado a
eles. Você deve criar um objeto Perfil manualmente usando o site de administração para
https://translate.googleusercontent.com/translate_f 129/504
16/12/2021 10:51 Sem título
os usuários que você criou antes.
Agora, você permitirá que os usuários editem seus perfis. Adicione o seguinte código ao mesmo arquivo:
@login_required
editar def (solicitação):
if request.method == 'POST':
user_form = UserEditForm (instância = request.user,
data = request.POST)
profile_form = ProfileEditForm (
instância = request.user.profile,
data = request.POST,
arquivos = solicitação.FILES)
if user_form.is_valid () e profile_form.is_valid ():
user_form.save ()
profile_form.save ()
senão:
user_form = UserEditForm (instância = request.user)
profile_form = ProfileEditForm (
instância = request.user.profile)
retornar render (pedido,
'account / edit.html',
{'user_form': user_form,
'profile_form': profile_form})
Finalmente, crie um modelo para esta visão em modelos / conta / e nomeie-o como edição.
html . Adicione o seguinte código a ele:
{% extends "base.html"%}
[ 123 ]
Página 149
{% block content%}
<h1> Edite sua conta </h1>
<p> Você pode editar sua conta usando o seguinte formulário: </p>
<form method = "post" enctype = "multipart / form-data">
{{user_form.as_p}}
{{profile_form.as_p}}
{% csrf_token%}
<p> <input type = "submit" value = "Salvar alterações"> </p>
</form>
{% endblock%}
https://translate.googleusercontent.com/translate_f 130/504
16/12/2021 10:51 Sem título
No código anterior, você inclui enctype = "multipart / form-data" em seu formulário
para habilitar uploads de arquivos. Você usa um formulário HTML para enviar o user_form e o
formulários profile_form .
[ 124 ]
Página 150
Capítulo 4
Você também pode editar a página do painel e incluir links para editar o perfil e
alterar páginas de senha. Abra o modelo account / dashboard.html e encontre
a seguinte linha:
<p> Bem-vindo ao seu painel. Você pode <a href="{% url "edit" %}"> editar
seu perfil </a> ou <a href="{% url "password_change" %}"> mude seu
senha </a>. </p>
Os usuários agora podem acessar o formulário para editar seus perfis em seus painéis. Aberto
http://127.0.0.1:8000/account/ em seu navegador e teste o novo link para editar um
perfil do usuário:
https://translate.googleusercontent.com/translate_f 131/504
16/12/2021 10:51 Sem título
Figura 4.17: Conteúdo da página do painel, incluindo links para editar um perfil e alterar uma senha
Usar um modelo de usuário personalizado lhe dará mais flexibilidade, mas também pode resultar
em uma integração mais difícil com aplicativos plugáveis que interagem com os do Django
modelo de usuário de autenticação .
[ 125 ]
Página 151
A estrutura de mensagens fornece uma maneira simples de adicionar mensagens aos usuários.
As mensagens são armazenadas em um cookie por padrão (voltando para o armazenamento da sessão), e
eles são exibidos na próxima solicitação do usuário. Você pode usar as mensagens
estrutura em suas visualizações, importando o módulo de mensagens e adicionando novos
mensagens com atalhos simples, como segue:
Você pode criar novas mensagens usando o método add_message () ou qualquer um dos
seguintes métodos de atalho:
• sucesso () : mensagens de sucesso a serem exibidas após uma ação ter sido
bem sucedido
• info () : mensagens informativas
• aviso () : Algo ainda não falhou, mas pode falhar iminentemente
• erro () : Uma ação não teve sucesso ou algo falhou
https://translate.googleusercontent.com/translate_f 132/504
16/12/2021 10:51 Sem título
• debug () : mensagens de depuração que serão removidas ou ignoradas em uma produção
ambiente
Vamos adicionar mensagens à sua plataforma. Uma vez que a estrutura de mensagens se aplica
globalmente para o projeto, você pode exibir mensagens para o usuário em seu modelo base.
Abra o modelo base.html do aplicativo de conta e adicione o seguinte
código entre o elemento <div> com o ID do cabeçalho e o elemento <div> com
o ID do conteúdo :
{% if messages%}
<ul class = "messages">
{% para mensagem em mensagens%}
<li class = "{{message.tags}}">
{{mensagem | segura}}
<a href="#" class="close"> x </a>
</li>
{% endfor%}
</ul>
{% fim se %}
[ 126 ]
Página 152
Capítulo 4
Vamos modificar sua visualização de edição para usar a estrutura de mensagens. Edite o views.py
arquivo do aplicativo de conta , importar mensagens e dar uma aparência de visualização de edição
do seguinte modo:
@login_required
editar def (solicitação):
if request.method == 'POST':
# ...
if user_form.is_valid () e profile_form.is_valid ():
user_form.save ()
profile_form.save ()
messages.success (request, 'Profile updated' \
'com sucesso')
senão:
messages.error (solicitação, 'Erro ao atualizar seu perfil')
senão:
user_form = UserEditForm (instância = request.user)
https://translate.googleusercontent.com/translate_f 133/504
16/12/2021 10:51 Sem título
# ...
Você adiciona uma mensagem de sucesso quando o usuário atualiza com sucesso seu perfil. Caso existam
dos formulários contêm dados inválidos, em vez disso, você adiciona uma mensagem de erro.
[ 127 ]
Página 153
Quando os dados não são válidos, por exemplo, se houver uma data formatada incorretamente para
o campo da data de nascimento , você deverá ver a seguinte mensagem:
['django.contrib.auth.backends.ModelBackend']
https://translate.googleusercontent.com/translate_f 134/504
16/12/2021 10:51 Sem título
como um diretório Lightweight Directory Access Protocol ( LDAP ) ou qualquer
outro sistema.
[ 128 ]
Página 154
Capítulo 4
Você pode ler mais informações sobre como personalizar a autenticação em https: //
docs.djangoproject.com/en/3.0/topics/auth/customizing/#other-
fontes de autenticação .
Django fornece uma maneira simples de definir seus próprios back-ends de autenticação. Um
back-end de autenticação é uma classe que fornece os dois métodos a seguir:
Criar um back-end de autenticação personalizado é tão simples quanto escrever uma classe Python
que implementa ambos os métodos. Vamos criar um back-end de autenticação para permitir que os usuários
autenticar em seu site usando seu endereço de e-mail em vez de seu nome de usuário.
[ 129 ]
https://translate.googleusercontent.com/translate_f 135/504
16/12/2021 10:51 Sem título
Página 155
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'account.authentication.EmailAuthBackend',
]
[ 130 ]
https://translate.googleusercontent.com/translate_f 136/504
16/12/2021 10:51 Sem título
Página 156
Capítulo 4
Este módulo vem com back-ends de autenticação para diferentes estruturas Python,
incluindo Django. Para instalar o pacote Django via pip , abra o console e execute
o seguinte comando:
INSTALLED_APPS = [
# ...
'social_django' ,
]
Este é o aplicativo padrão para adicionar Python Social Auth a projetos Django. Agora
execute o seguinte comando para sincronizar modelos de autenticação social Python com seu banco de dados:
Você verá que as migrações para o aplicativo padrão são aplicadas da seguinte forma:
...
O Python Social Auth inclui back-ends para vários serviços. Você pode ver uma lista de
todos os back-ends em https://python-social-auth.readthedocs.io/en/latest/
backends / index.html # supported-backends.
Você precisará adicionar padrões de URL de login social ao seu projeto. Abra os principais urls.
arquivo py do projeto de favoritos e inclui os padrões de URL social_django como
segue:
urlpatterns = [
caminho ('admin /', admin.site.urls),
caminho ('conta /', incluir ('conta.urls')),
caminho ('social-auth /',
include ('social_django.urls', namespace = 'social')),
]
[ 131 ]
Página 157
https://translate.googleusercontent.com/translate_f 137/504
16/12/2021 10:51 Sem título
Vários serviços sociais não permitirão o redirecionamento de usuários para 127.0.0.1 ou localhost
após uma autenticação bem-sucedida; eles esperam um nome de domínio. Para fazer
trabalho de autenticação social, você precisará de um domínio. Para corrigir isso no Linux ou macOS,
edite seu arquivo / etc / hosts e adicione a seguinte linha a ele:
127.0.0.1 mysite.com
Isso dirá ao seu computador para apontar o nome de host mysite.com para o seu
máquina. Se você estiver usando o Windows, o arquivo hosts está localizado em C: \ Windows \
System32 \ Drivers \ etc \ hosts .
Django controla os hosts que são capazes de servir a sua aplicação usando o ALLOWED_
Configuração HOSTS . Esta é uma medida de segurança para evitar ataques de cabeçalho de host HTTP.
O Django só permitirá que os hosts incluídos nesta lista sirvam o aplicativo. Você
pode saber mais sobre a configuração ALLOWED_HOSTS emhttps: //docs.djangoproject.
com / en / 3.0 / ref / settings / # allowed-hosts.
[ 132 ]
Página 158
Capítulo 4
https://translate.googleusercontent.com/translate_f 138/504
16/12/2021 10:51 Sem título
servidor não é capaz de servir seu site através de HTTPS, uma vez que não é esse o seu objetivo
usar. Para testar a funcionalidade de autenticação social servindo seu site por meio de
HTTPS, você vai usar a extensão RunServerPlus do pacote Django
Extensões. Django Extensions é uma coleção de terceiros de extensões personalizadas para
Django. Observe que esse nunca é o método que você deve usar para servir ao seu site
em um ambiente real; este é um servidor de desenvolvimento.
Agora você precisa instalar o Werkzeug, que contém uma camada de depuração exigida por
a extensão RunServerPlus. Use o seguinte comando para instalá-lo:
Finalmente, use o seguinte comando para instalar o pyOpenSSL, que é necessário para usar
a funcionalidade SSL / TLS do RunServerPlus:
INSTALLED_APPS = [
# ...
'django_extensions' ,
]
Você fornece um nome de arquivo para o comando runserver_plus para o SSL / TLS
certificado. As extensões do Django irão gerar uma chave e um certificado automaticamente.
[ 133 ]
Página 159
Você verá que o URL começa com https: // e um ícone de segurança que indica que
a conexão é segura.
Agora você pode servir seu site por meio de HTTPS durante o desenvolvimento para testar
autenticação social com Facebook, Twitter e Google.
https://translate.googleusercontent.com/translate_f 139/504
16/12/2021 10:51 Sem título
'social_core.backends.facebook.FacebookOAuth2',
No item de menu Meus Aplicativos , clique no botão Criar Aplicativo . Você vai ver o
seguinte formulário para criar um novo ID de aplicativo:
[ 134 ]
Página 160
Capítulo 4
https://translate.googleusercontent.com/translate_f 140/504
16/12/2021 10:51 Sem título
Insira os favoritos como o nome de exibição , adicione um endereço de e-mail de contato e clique em
Crie o ID do aplicativo . Você verá um painel de seu novo aplicativo que exibe
diferentes recursos que você pode configurar para ele. Procure a seguinte caixa de login do Facebook
e clique em Configurar :
[ 135 ]
Página 161
https://translate.googleusercontent.com/translate_f 141/504
16/12/2021 10:51 Sem título
[ 136 ]
Página 162
Capítulo 4
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
Agora, volte ao Facebook e clique em Configurações . Você verá um formulário com vários
configurações para seu aplicativo. Adicione mysite.com em App Domains , da seguinte maneira:
https://translate.googleusercontent.com/translate_f 142/504
16/12/2021 10:51 Sem título
• Aplicar HTTPS
• Login OAuth do navegador integrado
[ 137 ]
Página 163
Digite http://mysite.com:8000/social-auth/complete/facebook/ em
URIs de redirecionamento OAuth válidos . A seleção deve ser semelhante a esta:
https://translate.googleusercontent.com/translate_f 143/504
16/12/2021 10:51 Sem título
[ 138 ]
Página 164
Capítulo 4
Clique no botão Sign in with Facebook . Você será redirecionado para o Facebook,
e você verá uma caixa de diálogo modal pedindo sua permissão para permitir que os favoritos
aplicativo acesse seu perfil público do Facebook:
[ 139 ]
https://translate.googleusercontent.com/translate_f 144/504
16/12/2021 10:51 Sem título
Página 165
'social_core.backends.twitter.TwitterOAuth',
Você precisará criar um novo aplicativo em sua conta do Twitter. Abra https: //
developer.twitter.com/en/apps/create em seu navegador. Você será solicitado
várias perguntas para criar uma conta de desenvolvedor do Twitter, caso ainda não tenha feito isso.
Depois de ter uma conta de desenvolvedor, ao criar um novo aplicativo, você verá
o seguinte formulário:
[ 140 ]
https://translate.googleusercontent.com/translate_f 145/504
16/12/2021 10:51 Sem título
Página 166
Capítulo 4
• Site : https://mysite.com:8000/
• URL de retorno de chamada : https://mysite.com:8000/social-auth/complete/
Twitter/
Copie a chave de API e a chave secreta de API nas configurações a seguir em settings.py
arquivo do seu projeto:
[ 141 ]
Página 167
https://translate.googleusercontent.com/translate_f 146/504
16/12/2021 10:51 Sem título
'social_core.backends.google.GoogleOAuth2',
Primeiro, você precisará criar uma chave de API em seu Google Developer Console. Aberto
https://console.developers.google.com/apis/credentials em seu navegador.
Clique em Selecionar um projeto e em Novo projeto crie um novo projeto, da seguinte forma:
[ 142 ]
Página 168
Capítulo 4
https://translate.googleusercontent.com/translate_f 147/504
16/12/2021 10:51 Sem título
[ 143 ]
Página 169
A página anterior é a página que será mostrada aos usuários para dar seu consentimento
acessar seu site com sua conta do Google. Clique na tela Configurar consentimento
botão. Você será redirecionado para a seguinte tela:
https://translate.googleusercontent.com/translate_f 148/504
16/12/2021 10:51 Sem título
Figura 4.39: Seleção do tipo de usuário na configuração da tela de consentimento do Google OAuth
[ 144 ]
Página 170
Capítulo 4
https://translate.googleusercontent.com/translate_f 149/504
16/12/2021 10:51 Sem título
[ 145 ]
Página 171
https://translate.googleusercontent.com/translate_f 150/504
16/12/2021 10:51 Sem título
[ 146 ]
Página 172
Capítulo 4
https://translate.googleusercontent.com/translate_f 151/504
16/12/2021 10:51 Sem título
Figura 4.44: A página de login incluindo botões para autenticação do Twitter e Google
[ 147 ]
Página 173
Clique no botão Login com o Google . Você será conectado e redirecionado para
a página do painel do seu site.
Agora você adicionou autenticação social ao seu projeto. Você pode implementar facilmente
autenticação social com outros serviços online populares usando Python Social Auth.
Resumo
Neste capítulo, você aprendeu como construir um sistema de autenticação em seu site.
Você implementou todas as visualizações necessárias para que os usuários se registrem, façam login, façam logout,
editar sua senha e redefinir sua senha. Você construiu um modelo para usuário personalizado
perfis e você criou um back-end de autenticação personalizado para permitir que os usuários façam login no
seu site usando seu endereço de e-mail. Você também adicionou autenticação social ao seu
site para que os usuários possam usar sua conta existente do Facebook, Twitter ou Google para
Conecte-se.
https://translate.googleusercontent.com/translate_f 152/504
16/12/2021 10:51 Sem título
[ 148 ]
Página 174
Compartilhando conteúdo
5
no seu site
No capítulo anterior, você incorporou o registro do usuário e autenticação em seu
local na rede Internet. Você aprendeu como criar um modelo de perfil personalizado para seus usuários e para você
adicionou autenticação social ao seu site com as principais redes sociais.
Neste capítulo, você aprenderá como criar um bookmarklet JavaScript para compartilhar
conteúdo de outros sites em seu site, e você implementará recursos AJAX
em seu projeto usando jQuery e Django.
[ 149 ]
https://translate.googleusercontent.com/translate_f 153/504
16/12/2021 10:51 Sem título
Página 175
INSTALLED_APPS = [
# ...
'images.apps.ImagesConfig',
]
[ 150 ]
https://translate.googleusercontent.com/translate_f 154/504
16/12/2021 10:51 Sem título
Página 176
capítulo 5
Este é o modelo que você usará para armazenar imagens recuperadas de diferentes sites.
Vamos dar uma olhada nos campos deste modelo:
Você vai substituir o save () método da Imagem modelo para gerar automaticamente
o campo slug com base no valor do campo de título . Importe a função slugify ()
e adicionar um save () método para a imagem do modelo, como segue:
[ 151 ]
Página 177
https://translate.googleusercontent.com/translate_f 155/504
16/12/2021 10:51 Sem título
No código anterior, você usa a função slugify () fornecida pelo Django para
gerar automaticamente o slug de imagem para o título fornecido quando nenhum slug for fornecido.
Em seguida, você salva o objeto. Ao gerar slugs automaticamente, os usuários não têm
para inserir manualmente um slug para cada imagem.
Quando você define um ManyToManyField , o Django cria uma tabela de junção intermediária
usando as chaves primárias de ambos os modelos. O ManyToManyField pode ser definido em
qualquer um dos dois modelos relacionados.
Você pode aprender mais sobre relacionamentos muitos para muitos em https: // docs.
djangoproject.com/en/3.0/topics/db/examples/many_to_many/ .
[ 152 ]
Página 178
capítulo 5
https://translate.googleusercontent.com/translate_f 156/504
16/12/2021 10:51 Sem título
@ admin.register (imagem)
classe ImageAdmin (admin.ModelAdmin):
list_display = ['título', 'slug', 'imagem', 'criado']
list_filter = ['criado']
Vamos começar construindo um formulário para enviar novas imagens. Crie um novo forms.py
arquivo dentro do diretório do aplicativo Imagens e adicione o seguinte código a ele:
[ 153 ]
Página 179
https://translate.googleusercontent.com/translate_f 157/504
16/12/2021 10:51 Sem título
Como você notará no código anterior, este formulário é um formulário ModelForm construído a partir de
a imagem do modelo, incluindo apenas o título , url , e Descrição campos. Comercial
não irá inserir o URL da imagem diretamente no formulário. Em vez disso, você irá fornecê-los
com uma ferramenta JavaScript para escolher uma imagem de um site externo, e seu formulário
recebe seu URL como parâmetro. Você substitui o widget padrão do campo url para
use um widget HiddenInput . Este widget é renderizado como um elemento de entrada HTML
com um atributo type = "escondido" . Você usa este widget porque não quer isso
campo para ficar visível aos usuários.
No código anterior, você define um método clean_url () para limpar o campo url . O
o código funciona da seguinte maneira:
[ 154 ]
Página 180
capítulo 5
Além de validar o URL fornecido, você também precisa baixar o arquivo de imagem
e salve-o. Você poderia, por exemplo, usar a visão que lida com o formulário para fazer o download
o arquivo de imagem. Em vez disso, vamos adotar uma abordagem mais geral, substituindo o save ()
método de seu formulário de modelo para executar esta tarefa sempre que o formulário é salvo.
https://translate.googleusercontent.com/translate_f 158/504
16/12/2021 10:51 Sem título
para não
mas o banco de dados.
o salvará Se commit
no banco for False
de dados. Você ,substituirá
o método save () retornará
o método save ()uma instância do modelo
do seu
formulário para recuperar a imagem dada e salvá-la.
[ 155 ]
Página 181
1. Você cria uma nova instância de imagem chamando o método save () do formulário
com commit = False .
2. Você obtém a URL do dicionário cleaning_data do formulário.
3. Você gera o nome da imagem combinando o slug do título da imagem com o
extensão de arquivo original.
4. Você usa o módulo urllib do Python para baixar a imagem e, em seguida, chama o
método save () do campo de imagem, passando um objeto ContentFile que é
instanciado com o conteúdo do arquivo baixado. Desta forma, você salva o arquivo
para o diretório de mídia do seu projeto. Você passa o parâmetro save = False
para evitar salvar o objeto no banco de dados ainda.
5. Para manter o mesmo comportamento do método save () que você substituiu,
você salva o formulário no banco de dados apenas quando o parâmetro de confirmação é True .
Para usar o urllib para recuperar imagens de URLs servidos por meio de HTTPS,
você precisa instalar o pacote Certifi Python. Certifi é uma coleção de raiz
certificados para validar a confiabilidade dos certificados SSL / TLS.
https://translate.googleusercontent.com/translate_f 159/504
16/12/2021 10:51 Sem título
Você precisará de uma visão para lidar com o formulário. Edite o arquivo views.py das imagens
aplicativo e adicione o seguinte código a ele:
@login_required
def image_create (solicitação):
if request.method == 'POST':
# formulário foi enviado
form = ImageCreateForm (data = request.POST)
if form.is_valid ():
# dados do formulário são válidos
cd = form.cleaned_data
new_item = form.save (commit = False)
[ 156 ]
Página 182
capítulo 5
new_item.save ()
messages.success (pedido, 'Imagem adicionada com sucesso')
1. Você espera dados iniciais via GET para criar uma instância do formulário. Isto
os dados consistirão nos atributos de url e título de uma imagem de um
website e será fornecido via GET pela ferramenta JavaScript que você irá
criar mais tarde. Por enquanto, você apenas assume que esses dados estarão lá inicialmente.
2. Se o formulário for enviado, você verifica se ele é válido. Se os dados do formulário forem válidos,
você cria uma nova instância de Image , mas evita que o objeto seja salvo em
o banco de dados ainda passando commit = False para o método save () do formulário .
3. Você atribui o usuário atual ao novo objeto de imagem . É assim que você pode
saiba quem carregou cada imagem.
https://translate.googleusercontent.com/translate_f 160/504
16/12/2021 10:51 Sem título
faça isso mais tarde.
Crie um novo arquivo urls.py dentro do aplicativo de imagens e adicione o seguinte código
para isso:
app_name = 'imagens'
urlpatterns = [
caminho ('criar /', views.image_create, name = 'criar'),
]
[ 157 ]
Página 183
Edite o arquivo urls.py principal do projeto de favoritos para incluir os padrões para o
aplicativo de imagens , da seguinte forma:
urlpatterns = [
caminho ('admin /', admin.site.urls),
caminho ('conta /', incluir ('conta.urls')),
caminho ('social-auth /',
include ('social_django.urls', namespace = 'social')),
caminho ('images /', include ('images.urls', namespace = 'images')),
]
Finalmente, você precisa criar um modelo para renderizar o formulário. Crie o seguinte
estrutura do diretório dentro do diretório do aplicativo de imagens :
modelos/
imagens /
imagem/
create.html
{% extends "base.html"%}
{% block content%}
<h1> Marque uma imagem </h1>
<img src = "{{request.GET.url}}" class = "image-preview">
<form method = "post">
{{form.as_p}}
{% csrf_token%}
<input type = "submit" value = "Bookmark it!">
</form>
{% endblock%}
https://translate.googleusercontent.com/translate_f 161/504
16/12/2021 10:51 Sem título
Duke & url = https: //upload.wikimedia.org/wikipedia/commons/8/85/Django_
Reinhardt_and_Duke_Ellington_% 28Gottlieb% 29.jpg .
[ 158 ]
Página 184
capítulo 5
Adicione uma descrição e clique no botão BOOKMARK IT! botão. Uma nova imagem objeto
será salvo em seu banco de dados. No entanto, você receberá um erro que indica que
a Imagem modelo não tem get_absolute_url () método, como segue:
Figura 5.3: Um erro mostrando que o objeto Imagem não possui o atributo get_absolute_url
Não se preocupe com esse erro por enquanto; você irá adicionar um get_absolute_url
método para a imagem do modelo mais tarde.
Figura 5.4: A página da lista de imagens do site de administração mostrando o objeto Imagem criado
[ 159 ]
https://translate.googleusercontent.com/translate_f 162/504
16/12/2021 10:51 Sem título
Página 185
Alguns serviços online, como o Pinterest, implementam seus próprios bookmarklets para permitir
os usuários compartilham conteúdo de outros sites em sua plataforma. Vamos criar um bookmarklet
de forma semelhante para seu site, usando jQuery para construir seu bookmarklet.
jQuery é uma biblioteca JavaScript popular que permite que você desenvolva
funcionalidade lateral mais rápida. Você pode ler mais sobre jQuery em seu site oficial:
https://jquery.com/ .
Uma vez que o código JavaScript será armazenado como um favorito, você não será capaz de
atualize-o mais tarde. Esta é uma desvantagem importante que você pode resolver implementando
um script de iniciador para carregar o bookmarklet JavaScript real de um URL. Seus usuários
irá salvar este script de iniciador como um favorito, e você será capaz de atualizar o código
do bookmarklet a qualquer momento. Esta é a abordagem que você usará para construir seu
bookmarklet. Vamos começar!
(função(){
if (window.myBookmarklet! == undefined) {
myBookmarklet ();
}
senão {
document.body.appendChild (document.createElement ('script')).
src = 'https: //127.0.0.1:? 8000 / static / js / bookmarklet.js r =' + Math.
floor (Math.random () * 99999999999999999999);
}
}) ();
[ 160 ]
https://translate.googleusercontent.com/translate_f 163/504
16/12/2021 10:51 Sem título
Página 186
capítulo 5
Vamos adicionar o iniciador de bookmarklet às páginas do painel para que seus usuários possam
copie-o para seus favoritos. Edite o modelo account / dashboard.html do
aplicativo de conta e torná-lo parecido com o seguinte:
{% extends "base.html"%}
{% block content%}
<h1> Painel </h1>
<p> Arraste o seguinte botão para a barra de ferramentas de favoritos para adicionar como favorito
imagens de outros sites → <a href = "javascript: {% incluem
"bookmarklet_launcher.js"%} "class =" button "> Adicione aos favoritos </a> </p>
<p> Você também pode <a href="{% url "edit" %}"> editar seu perfil </a>
ou <a href="{% url "password_change" %}"> altere sua senha </a>. </p>
{% endblock%}
Certifique-se de que nenhuma tag de modelo seja dividida em várias linhas; Django não suporta
várias marcas de linha.
[ 161 ]
Página 187
https://translate.googleusercontent.com/translate_f 164/504
16/12/2021 10:51 Sem título
Figura 5.5: A página do painel, incluindo o total de imagens favoritas e o botão para o bookmarklet
estático/
js /
bookmarklet.js
(função(){
var jquery_version = '3.4.1';
var site_url = 'https://127.0.0.1:8000/';
var static_url = site_url + 'static /';
var min_width = 100;
var min_height = 100;
[ 162 ]
Página 188
capítulo 5
https://translate.googleusercontent.com/translate_f 165/504
16/12/2021 10:51 Sem título
} senão {
// Verifique se há conflitos
var conflito = tipo de janela. $! = 'indefinido';
// Crie o script e aponte para a API do Google
var script = document.createElement ('script');
script.src = '//ajax.googleapis.com/ajax/libs/jquery/' +
jquery_version + '/jquery.min.js';
// Adicione o script à 'cabeça' para processamento
document.head.appendChild (script);
// Crie uma maneira de esperar até o carregamento do script
tentativas de var = 15;
(função(){
// Verifique novamente se jQuery é indefinido
if (typeof window.jQuery == 'undefined') {
if (- tentativas> 0) {
// Chama a si mesmo em alguns milissegundos
window.setTimeout (arguments.callee, 250)
} senão {
// Muitas tentativas de carregar, enviar erro
alert ('Ocorreu um erro ao carregar o jQuery')
}
} senão {
bookmarklet ();
}
}) ();
}
}) ()
Este é o principal script do carregador jQuery. Ele cuida de usar jQuery se já tiver
foi carregado no site atual. Se o jQuery não estiver carregado, o script carrega o jQuery
da rede de distribuição de conteúdo ( CDN ) do Google, que hospeda o popular JavaScript
frameworks. Quando o jQuery é carregado, ele executa a função bookmarklet () que
conterá o código do seu bookmarklet. Além disso, defina algumas variáveis na parte superior do arquivo:
[ 163 ]
Página 189
https://translate.googleusercontent.com/translate_f 166/504
16/12/2021 10:51 Sem título
aleatório () * 99999999999999999999)
});
jQuery ('cabeça'). append (css);
// carregar HTML
box_html = '<div id = "bookmarklet"> <a href="#" id="close"> & times; </
a> <h1> Selecione uma imagem para marcar: </h1> <div class = "images"> </div> </
div> ';
jQuery ('corpo'). append (box_html);
// fechar evento
jQuery ('# bookmarklet #close'). click (function () {
jQuery ('# bookmarklet'). remove ();
});
};
Depois de carregar os estilos CSS e o código HTML para o bookmarklet, você precisará
para encontrar as imagens no site. Adicione o seguinte código JavaScript na parte inferior
da função bookmarklet () :
[ 164 ]
Página 190
capítulo 5
O código anterior usa o seletor img [src $ = "jpg"] para encontrar todos os <img> HTML
elementos cujo atributo src termina com uma string jpg . Isso significa que você vai
pesquisar todas as imagens JPEG exibidas no site atual. Você itera sobre o
resultados usando o método each () de jQuery. Você adiciona as imagens com um tamanho maior
do que o especificado com os MIN_WIDTH e MIN_HEIGHT variáveis para a sua <div
class = "images"> container HTML.
https://translate.googleusercontent.com/translate_f 167/504
16/12/2021 10:51 Sem título
[ 165 ]
Página 191
https://translate.googleusercontent.com/translate_f 168/504
16/12/2021 10:51 Sem título
Figura 5.7: O bookmarklet carregado em um site externo
O contêiner HTML inclui as imagens que podem ser marcadas. Você quer o
usuário clicar na imagem desejada e marcá-la. Edite o js / bookmarklet.js
arquivo estático e adicione o seguinte código na parte inferior da função bookmarklet () :
[ 166 ]
Página 192
capítulo 5
Abra um novo URL com o seu navegador e clique no seu bookmarklet novamente para exibir
a caixa de seleção de imagem. Se você clicar em uma imagem, você será redirecionado para a imagem
criar página, passando o título do site e a URL da imagem selecionada como GET
parâmetros:
https://translate.googleusercontent.com/translate_f 169/504
16/12/2021 10:51 Sem título
[ 167 ]
Página 193
Esta é uma visão simples para exibir uma imagem. Edite o arquivo urls.py das imagens
aplicativo e adicione o seguinte padrão de URL:
Lembre-se de que o padrão comum para fornecer URLs canônicos para objetos é
para definir um método get_absolute_url () no modelo.
https://translate.googleusercontent.com/translate_f 170/504
16/12/2021 10:51 Sem título
[ 168 ]
Página 194
capítulo 5
{% extends "base.html"%}
{% block content%}
<h1> {{image.title}} </h1>
<img src = "{{image.image.url}}" class = "detalhe da imagem">
{% with total_likes = image.users_like.count%}
<div class = "image-info">
<div>
<span class = "count">
{{total_likes}} como {{total_likes | pluralize}}
</span>
</div>
{{image.description | quebras de linha}}
</div>
<div class = "image-likes">
{% para o usuário em image.users_like.all%}
<div>
<img src = "{{user.profile.photo.url}}">
<p> {{user.first_name}} </p>
</div>
{% vazio %}
Ninguém gosta desta imagem ainda.
{% endfor%}
</div>
{% endwith%}
{% endblock%}
Este é o modelo para exibir a visualização de detalhes de uma imagem marcada. Você faz
uso da tag {% with%} para armazenar o resultado do QuerySet, contando todas as curtidas do usuário
em uma nova variável chamada total_likes . Ao fazer isso, você evita avaliar o mesmo
QuerySet duas vezes. Você também inclui a descrição da imagem e itera sobre a imagem.
users_like.all para exibir todos os usuários que gostaram desta imagem.
[ 169 ]
https://translate.googleusercontent.com/translate_f 171/504
16/12/2021 10:51 Sem título
Página 195
Sempre que você precisar repetir uma consulta em seu modelo, use o
{% with%} template tag para evitar consultas adicionais ao banco de dados.
Em seguida, marque uma nova imagem usando o bookmarklet. Você será redirecionado para o
página de detalhes da imagem depois de postar a imagem. A página incluirá uma mensagem de sucesso,
do seguinte modo:
[ 170 ]
https://translate.googleusercontent.com/translate_f 172/504
16/12/2021 10:51 Sem título
Página 196
capítulo 5
INSTALLED_APPS = [
# ...
'easy_thumbnails',
]
Em seguida, execute o seguinte comando para sincronizar o aplicativo com seu banco de dados:
{% load thumbnail%}
<a href="{{ image.image.url }}">
<img src = "{% thumbnail image.image 300x0%}" class = "image-detail">
</a>
Você define uma miniatura com largura fixa de 300 pixels e altura flexível para
mantenha a proporção da imagem usando o valor 0 . Na primeira vez que um usuário carrega esta página,
uma imagem em miniatura é criada. A miniatura é armazenada no mesmo diretório do
arquivo original. A localização é definida pela configuração MEDIA_ROOT e o upload_to
atributo da imagem campo da Imagem modelo.
[ 171 ]
Página 197
https://translate.googleusercontent.com/translate_f 173/504
16/12/2021 10:51 Sem título
Você pode usar um valor de qualidade diferente usando o parâmetro de qualidade . Para definir o mais alto
Qualidade JPEG, você pode usar o valor 100 , como este {% thumbnail image.image
Qualidade 300x0 = 100%} .
Você adicionará um link para a página de detalhes da imagem para permitir que os usuários cliquem nela para
como uma imagem. Você executará esta ação com uma chamada AJAX para evitar recarregar o
página inteira.
Primeiro, crie uma visualização para os usuários gostarem / não gostarem das imagens. Edite o arquivo views.py do
aplicativo de imagens e adicione o seguinte código a ele:
@login_required
@require_POST
def image_like (solicitação):
image_id = request.POST.get ('id')
action = request.POST.get ('ação')
se id_imagem e ação:
experimentar:
image = Image.objects.get (id = image_id)
if action == 'like':
image.users_like.add (request.user)
senão:
image.users_like.remove (request.user)
return JsonResponse ({'status': 'ok'})
exceto:
[ 172 ]
Página 198
capítulo 5
passar
retornar JsonResponse ({'status': 'erro'})
https://translate.googleusercontent.com/translate_f 174/504
16/12/2021 10:51 Sem título
Você usa dois decoradores para a sua visualização. O decorador login_required impede
usuários que não estão logados para acessar esta visão. O decorador require_POST
retorna um objeto HttpResponseNotAllowed (código de status 405 ) se a solicitação HTTP
não é feito via POST . Dessa forma, você só permite solicitações POST para esta visualização.
Django também fornece um decorador require_GET para permitir apenas solicitações GET e
um decorador require_http_methods para o qual você pode passar uma lista de métodos permitidos
como um argumento.
Você usa o gerenciador fornecido pelo Django para o campo users_like muitos para muitos
do modelo de imagem para adicionar ou remover objetos do relacionamento usando o
métodos add () ou remove () . Chamar add () , ou seja, passar um objeto que já está
presente no conjunto de objetos relacionado, não o duplica. Chamando remove () e passando
um objeto que não está no conjunto de objetos relacionados não faz nada. Outro método útil
de muitos para muitos gerenciadores é clear () , que remove todos os objetos do relacionado
conjunto de objetos.
Finalmente, você usa a classe JsonResponse fornecida pelo Django, que retorna um
Resposta HTTP com um tipo de conteúdo application / json , convertendo o objeto fornecido
em uma saída JSON.
Edite o arquivo urls.py do aplicativo de imagens e adicione o seguinte padrão de URL a ele:
Carregando jQuery
Você precisará adicionar a funcionalidade AJAX ao seu modelo de detalhes de imagem. Em ordem
para usar jQuery em seus modelos, você irá incluí-lo no modelo base.html de
seu projeto primeiro. Edite o modelo base.html do aplicativo de conta e
inclua o seguinte código antes da tag HTML </body> de fechamento :
[ 173 ]
Página 199
{% block domready%}
{% endblock%}
});
</script>
Você carrega o framework jQuery do CDN do Google. Você também pode baixar o jQuery
em https://jquery.com/ e adicione-o ao diretório estático do seu aplicativo
em vez de.
Você adiciona uma tag <script> para incluir o código JavaScript. $ (document) .ready () é
https://translate.googleusercontent.com/translate_f 175/504
16/12/2021 10:51 Sem título
uma
A função jQuery
hierarquia que leva
do modelo um manipulador
( DOM queconstruída.
) foi totalmente é executadoO quando
DOM é ocriado
objetopelo
de documento
navegador quando uma página da web é carregada e é construída como uma árvore de objetos. Por
incluindo seu código dentro desta função, você se certificará de que todos os elementos HTML
com os quais você vai interagir são carregados no DOM. Seu código será apenas
executado assim que o DOM estiver pronto.
Dentro da função de manipulador pronto para documentos, você inclui um bloco de template Django
chamado domready , em que os modelos que estendem o modelo básico serão capazes de
incluem JavaScript específico. Não se confunda com o código JavaScript e Django
tags de modelo. A linguagem de template do Django é renderizada no lado do servidor, gerando
o documento HTML final e o JavaScript é executado no lado do cliente. Em alguns casos,
é útil gerar código JavaScript dinamicamente usando Django, para ser capaz de usar o
resultados de QuerySets ou cálculos do lado do servidor para definir variáveis em JavaScript.
Para incluir o token em todas as solicitações, você precisa seguir as seguintes etapas:
[ 174 ]
Página 200
capítulo 5
Você pode encontrar mais informações sobre proteção CSRF e AJAX em https: // docs.
djangoproject.com/en/3.0/ref/csrf/#ajax .
Edite o último código que você incluiu em seu modelo base.html e faça com que pareça
a seguir:
https://translate.googleusercontent.com/translate_f 176/504
16/12/2021 10:51 Sem título
1. Você carrega o plugin JS Cookie de um CDN público para que possa facilmente
interagir com os cookies. JS Cookie é uma API JavaScript leve para lidar com
biscoitos. Você pode aprender mais sobre isso emhttps://github.com/js-cookie/
js-cookie.
2. Você leu o valor do cookie csrftoken com Cookies.get () .
3. Você define a função csrfSafeMethod () para verificar se um HTTP
método é seguro. Métodos seguros não requerem proteção contra CSRF - estes são GET ,
CABEÇA , OPÇÕES e TRACE .
4. Você configura as solicitações jQuery AJAX usando $ .ajaxSetup () . Antes de cada AJAX
a solicitação é realizada, você verifica se o método de solicitação é seguro e se
a solicitação atual não é entre domínios. Se a solicitação não for segura, você define o
Cabeçalho X-CSRFToken com o valor obtido do cookie. Esta configuração
se aplicará a todas as solicitações AJAX realizadas com jQuery.
[ 175 ]
Página 201
O token CSRF será incluído em todas as solicitações AJAX que usam HTTP não seguro
métodos, como POST ou PUT .
com o seguinte:
https://translate.googleusercontent.com/translate_f 177/504
16/12/2021 10:51 Sem título
<div class = "image-info">
<div>
<span class = "count">
<span class = "total"> {{total_likes}} </span>
como {{total_likes | pluralize}}
</span>
<a href = "#" data-id = "{{image.id}}" data-action = "{% if
request.user em users_like%} un {% endif%} like "
class = "botão curtir">
{% if request.user não está em users_like%}
Como
{% senão %}
diferente
{% fim se %}
</a>
</div>
{{image.description | quebras de linha}}
</div>
Primeiro, você adiciona outra variável à tag de modelo {% with%} para armazenar o
resultados da consulta image.users_like.all e evite executá-la duas vezes. Você usa
a variável para o loop for que itera sobre os usuários que gostam desta imagem.
[ 176 ]
Página 202
capítulo 5
Você exibe o número total de usuários que gostam da imagem e inclui um link para curtir /
ao contrário da imagem. Você verifica se o usuário está no conjunto de objetos relacionados de users_
gosto de exibir como ou não , com base na relação atual entre o
usuário e esta imagem. Você adiciona os seguintes atributos ao elemento HTML <a> :
{% block domready%}
$ ('a.like'). click (function (e) {
e.preventDefault ();
https://translate.googleusercontent.com/translate_f 178/504
16/12/2021 10:51 Sem título
$ .post ('{% url "imagens: como"%}',
{
id: $ (this) .data ('id'),
ação: $ (this) .data ('ação')
},
função (dados) {
if (dados ['status'] == 'ok')
{
var previous_action = $ ('a.like'). data ('action');
// toggle data-action
$ ('a.like'). data ('action', previous_action == 'like'?
'diferente de': 'como');
[ 177 ]
Página 203
1. Você usa o seletor $ ('a.like') jQuery para encontrar todos os elementos <a> do
Documento HTML com a classe like .
2. Você define uma função de manipulador para o evento click . Esta função será
executado toda vez que o usuário clica no link curtir / não curtir.
3. Dentro da função de manipulador, você usa e.preventDefault () para evitar o
comportamento padrão do elemento <a> . Isso impedirá que o link pegue
você em qualquer lugar.
4. Use $ .post () para executar uma solicitação POST assíncrona para o servidor.
jQuery também fornece um método $ .get () para realizar solicitações GET e um baixo
método nível $ .ajax () .
5. Use a tag de modelo {% url%} do Django para construir a URL para o AJAX
solicitar.
6. Você constrói o dicionário de parâmetros POST para enviar a solicitação. O
parâmetros são os parâmetros de ID e ação esperados por sua visão Django.
Você recupera esses valores do elemento <a> data-id e data-action
atributos.
7. Você define uma função de retorno de chamada que é executada quando a resposta HTTP é
recebido; leva um atributo de dados que contém o conteúdo da resposta.
8. Você acessa o atributo de status dos dados recebidos e verifica se
https://translate.googleusercontent.com/translate_f 179/504
16/12/2021 10:51 Sem título
é igual a ok . Se os dados retornados forem os esperados, você alterna a ação de dados
atributo do link e seu texto. Isso permite que o usuário desfaça sua ação.
9. Você aumenta ou diminui a contagem total de curtidas em um, dependendo da ação
realizada.
[ 178 ]
Página 204
capítulo 5
Abra a página de detalhes da imagem em seu navegador para uma imagem que você carregou.
Você deve ser capaz de ver a seguinte contagem inicial de gostos e o botão GOSTO ,
do seguinte modo:
Clique no botão CURTIR . Você notará que a contagem total de curtidas aumenta em um
e o texto do botão muda para UNLIKE , da seguinte forma:
[ 179 ]
https://translate.googleusercontent.com/translate_f 180/504
16/12/2021 10:51 Sem título
Página 205
Como seu decorador será genérico e pode ser aplicado a qualquer visualização, você
crie um pacote Python comum em seu projeto. Crie o seguinte diretório
e arquivos dentro do diretório do projeto de favoritos :
comum/
__init__.py
decorators.py
Agora você pode editar o arquivo views.py do aplicativo de imagens e adicionar este decorador
para sua visualização image_like AJAX, da seguinte maneira:
@ajax_required
@login_required
@require_POST
def image_like (solicitação):
# ...
Construa decoradores personalizados para suas visualizações se você achar que está
repetir as mesmas verificações em várias visualizações.
[ 180 ]
https://translate.googleusercontent.com/translate_f 181/504
16/12/2021 10:51 Sem título
Página 206
capítulo 5
Vamos implementar uma visualização de lista de imagens que irá lidar com as solicitações padrão do navegador
e solicitações AJAX, incluindo paginação. Quando o usuário carrega inicialmente a imagem
página de lista, você exibirá a primeira página de imagens. Quando eles rolam para o fundo
da página, você irá carregar a seguinte página de itens via AJAX e anexá-la a
a parte inferior da página principal.
@login_required
def image_list (solicitação):
images = Image.objects.all ()
paginator = Paginator (imagens, 8)
page = request.GET.get ('page')
experimentar:
images = paginator.page (página)
exceto PageNotAnInteger:
# Se a página não for um inteiro, entregue a primeira página
images = paginator.page (1)
exceto EmptyPage:
if request.is_ajax ():
# Se a solicitação for AJAX e a página estiver fora do intervalo
# devolver uma página vazia
retornar HttpResponse ('')
# Se a página estiver fora do intervalo, entregue a última página de resultados
images = paginator.page (paginator.num_pages)
if request.is_ajax ():
retornar render (pedido,
'images / image / list_ajax.html',
{'seção': 'imagens', 'imagens': imagens})
retornar render (pedido,
'images / image / list.html',
{'seção': 'imagens', 'imagens': imagens})
[ 181 ]
Página 207
https://translate.googleusercontent.com/translate_f 182/504
16/12/2021 10:51 Sem título
Nesta visualização, você cria um QuerySet para retornar todas as imagens do banco de dados. Então você
construir um objeto Paginator para paginar os resultados, recuperando oito imagens por página.
Você obterá uma exceção EmptyPage se a página solicitada estiver fora do intervalo. Se este for o
caso e a solicitação é feita via AJAX, você retorna um HttpResponse vazio que irá
ajudá-lo a parar a paginação AJAX no lado do cliente. Você renderiza os resultados para
dois modelos diferentes:
Edite o arquivo urls.py do aplicativo de imagens e adicione o seguinte padrão de URL a ele:
Finalmente, você precisa criar os modelos mencionados aqui. Dentro das imagens / imagem /
diretório de modelo, crie um novo modelo e nomeie-o list_ajax.html . Adicione o
seguinte código para ele:
{% load thumbnail%}
O modelo anterior exibe a lista de imagens. Você o usará para retornar resultados
para solicitações AJAX. Neste código, você itera sobre as imagens e gera um quadrado
miniatura para cada imagem. Você normaliza o tamanho das miniaturas para 300 x 300
píxeis. Você também pode usar a opção de corte inteligente . Esta opção indica que a imagem
deve ser cortado gradativamente até o tamanho solicitado, removendo fatias de
as bordas com a menor entropia.
[ 182 ]
Página 208
capítulo 5
https://translate.googleusercontent.com/translate_f 183/504
16/12/2021 10:51 Sem título
{% extends "base.html"%}
{% block content%}
<h1> Imagens marcadas </h1>
<div id = "image-list">
{% include "images / image / list_ajax.html"%}
</div>
{% endblock%}
O modelo de lista estende o modelo base.html . Para evitar a repetição do código, você
inclua o modelo list_ajax.html para exibir imagens. O list.html
modelo irá conter o código JavaScript para carregar páginas adicionais ao rolar
para o final da página.
{% block domready%}
var page = 1;
var empty_page = false;
var block_request = false;
[ 183 ]
Página 209
https://translate.googleusercontent.com/translate_f 184/504
16/12/2021 10:51 Sem título
que não há mais resultados.
° block_request : impede que você envie solicitações adicionais
enquanto uma solicitação AJAX está em andamento.
2. Você usa $ (window) .scroll () para capturar o evento de rolagem e também para definir
uma função de manipulador para ele.
3. Você calcula a variável de margem para obter a diferença entre o total
altura do documento e a altura da janela, porque essa é a altura do
conteúdo restante para o usuário rolar. Você subtrai um valor de 200 do
resultado para que você carregue a próxima página quando o usuário estiver mais próximo do que 200 pixels
para o final da página.
4. Você só envia uma solicitação AJAX se nenhuma outra solicitação AJAX estiver sendo feita
( block_request tem que ser falso ) e o usuário não chegou à última página
de resultados ( empty_page também é falso ).
5. Você define block_request como true para evitar uma situação em que o evento scroll
aciona solicitações AJAX adicionais e aumenta o contador de páginas em um,
para recuperar a próxima página.
6. Você executa uma solicitação AJAX GET usando $ .get () e recebe o HTML
resposta em uma variável chamada dados . A seguir estão os dois cenários:
° A resposta não tem conteúdo : você chegou ao fim dos resultados,
e não há mais páginas para carregar. Você definiu empty_page como true
para evitar solicitações AJAX adicionais.
° A resposta contém dados : você anexa os dados ao HTML
elemento com o ID da lista de imagens . O conteúdo da página se expande
verticalmente, acrescentando resultados quando o usuário se aproxima da parte inferior
da página.
[ 184 ]
Página 210
capítulo 5
https://translate.googleusercontent.com/translate_f 185/504
16/12/2021 10:51 Sem título
Role até a parte inferior da página para carregar páginas adicionais. Certifique-se de que você tem
marcou mais de oito imagens usando o bookmarklet, porque esse é o
número de imagens que você está exibindo por página. Lembre-se de que você pode usar o Firebug
ou uma ferramenta semelhante para rastrear as solicitações AJAX e depurar seu código JavaScript.
[ 185 ]
Página 211
Resumo
Neste capítulo, você criou modelos com relacionamentos muitos para muitos e aprendeu
como personalizar o comportamento dos formulários. Você usou jQuery com Django para construir
um bookmarklet JavaScript para compartilhar imagens de outros sites em seu site. Isto
capítulo também cobriu a criação de miniaturas de imagens usando as miniaturas fáceis
biblioteca. Por fim, você implementou visualizações AJAX com jQuery e adicionou AJAX
paginação para a visualização da lista de imagens.
No próximo capítulo, você aprenderá como construir um sistema de acompanhamento e uma atividade
Stream. Você trabalhará com relações, sinais e desnormalização genéricos. Você
também aprenderá como usar Redis com Django.
https://translate.googleusercontent.com/translate_f 186/504
16/12/2021 10:51 Sem título
[ 186 ]
Página 212
Neste capítulo, você aprenderá como construir um sistema de acompanhamento e criar uma atividade de usuário
Stream. Você também descobrirá como os sinais do Django funcionam e integram o sistema rápido do Redis
Armazenamento de E / S em seu projeto para armazenar visualizações de itens.
https://translate.googleusercontent.com/translate_f 187/504
16/12/2021 10:51 Sem título
• Otimizando QuerySets para objetos relacionados
• Usando sinais para desnormalizar contagens
• Armazenamento de visualizações de itens no Redis
[ 187 ]
Página 213
• Você está usando o modelo de usuário fornecido pelo Django e deseja evitar
alterando isso
• Você deseja armazenar a hora em que o relacionamento foi criado
Edite o arquivo models.py do seu aplicativo de conta e adicione o seguinte código a ele:
classe Meta:
ordenação = ('-criado',)
O código anterior mostra o modelo de contato que você usará para o usuário
relacionamentos. Ele contém os seguintes campos:
https://translate.googleusercontent.com/translate_f 188/504
16/12/2021 10:51 Sem título
• user_from : Uma ForeignKey para o usuário que cria o relacionamento
• user_to : Uma ForeignKey para o usuário que está sendo seguido
[ 188 ]
Página 214
Capítulo 6
Um índice de banco de dados é criado automaticamente nos campos ForeignKey . Você usa db_
index = True para criar um índice de banco de dados para o campo criado . Isso vai melhorar
desempenho de consulta ao ordenar QuerySets por este campo.
Usando o ORM, você pode criar um relacionamento para um usuário, usuário1 , seguindo outro
usuário, usuário2 , assim:
No exemplo anterior, você diz ao Django para usar seu modelo intermediário personalizado
para o relacionamento adicionando through = Contact ao ManyToManyField . Isto
é um relacionamento muitos-para-muitos do modelo de usuário para ele mesmo; você se refere a 'eu'
no campo ManyToManyField para criar um relacionamento com o mesmo modelo.
Se o modelo de usuário fazia parte do seu aplicativo, você poderia adicionar o campo anterior
para o modelo. No entanto, você não pode alterar a classe User diretamente porque ela pertence ao
o aplicativo django.contrib.auth . Vamos fazer uma abordagem um pouco diferente
adicionando este campo dinamicamente ao modelo de usuário .
[ 189 ]
https://translate.googleusercontent.com/translate_f 189/504
16/12/2021 10:51 Sem título
Página 215
[ 190 ]
https://translate.googleusercontent.com/translate_f 190/504
16/12/2021 10:51 Sem título
Página 216
Capítulo 6
Agora, execute o seguinte comando para sincronizar o aplicativo com o banco de dados:
O modelo de contato agora está sincronizado com o banco de dados e você pode criar
relações entre os usuários. No entanto, seu site não oferece uma maneira de navegar
usuários ou ver o perfil de um determinado usuário ainda. Vamos construir listas e visualizações de detalhes para o
Modelo de usuário .
@login_required
def user_list (solicitação):
users = User.objects.filter (is_active = True)
retornar render (pedido,
'account / user / list.html',
{'seção': 'pessoas',
'usuários': usuários})
@login_required
def user_detail (solicitação, nome de usuário):
user = get_object_or_404 (usuário,
username = username,
is_active = True)
retornar render (pedido,
'account / user / detail.html',
{'seção': 'pessoas',
'usuário': usuário})
Estas são listas simples e visualizações de detalhes para objetos de Usuário . A visão user_list
obtém todos os usuários ativos. O modelo Django User contém um sinalizador is_active para
designar se a conta do usuário é considerada ativa. Você filtra a consulta por
is_active = True para retornar apenas usuários ativos. Esta visualização retorna todos os resultados, mas você
pode melhorá-lo adicionando paginação da mesma forma que você fez para a imagem_
visualização de lista .
[ 191 ]
Página 217
https://translate.googleusercontent.com/translate_f 191/504
16/12/2021 10:51 Sem título
Edite o arquivo urls.py do aplicativo da conta e adicione um padrão de URL para cada
vista, da seguinte forma:
urlpatterns = [
# ...
caminho ('users /', views.user_list, name = 'user_list'),
caminho ('users / <username> /', views.user_detail, name = 'user_detail'),
]
Você usará o padrão de URL user_detail para gerar a URL canônica para os usuários.
Você já definiu um método get_absolute_url () em um modelo para retornar o
URL canônico para cada objeto. Outra maneira de especificar o URL de um modelo é por
adicionando a configuração ABSOLUTE_URL_OVERRIDES ao seu projeto.
ABSOLUTE_URL_OVERRIDES = {
'auth.user': lambda u: reverse_lazy ('user_detail',
args = [u.username])
}
[ 192 ]
Página 218
Capítulo 6
Você precisará criar modelos para as visualizações que acabou de construir. Adicione o seguinte
https://translate.googleusercontent.com/translate_f 192/504
16/12/2021 10:51 Sem título
diretório e arquivos para os modelos / conta / diretório do aplicativo de conta :
/do utilizador/
detail.html
list.html
{% extends "base.html"%}
{% load thumbnail%}
{% block content%}
<h1> Pessoas </h1>
<div id = "lista de pessoas">
{% para usuário em usuários%}
<div class = "user">
<a href="{{ user.get_absolute_url }}">
<img src = "{% thumbnail user.profile.photo 180x180%}">
</a>
<div class = "info">
<a href="{{ user.get_absolute_url }}" class="title">
{{user.get_full_name}}
</a>
</div>
</div>
{% endfor%}
</div>
{% endblock%}
O modelo anterior permite que você liste todos os usuários ativos no site. Você
itere sobre os usuários fornecidos e use a tag de modelo {% thumbnail%} do easy-
miniaturas para gerar miniaturas de imagens de perfil.
[ 193 ]
Página 219
https://translate.googleusercontent.com/translate_f 193/504
16/12/2021 10:51 Sem título
{% extends "base.html"%}
{% load thumbnail%}
{% block content%}
<h1> {{user.get_full_name}} </h1>
[ 194 ]
Página 220
Capítulo 6
https://translate.googleusercontent.com/translate_f 194/504
16/12/2021 10:51 Sem título
</a>
<div id = "image-list" class = "image-container">
{% inclua "images / image / list_ajax.html" com imagens = usuário.
images_created.all%}
</div>
{% endwith%}
{% endblock%}
Certifique-se de que nenhuma tag de modelo seja dividida em várias linhas; Django não suporta
várias marcas de linha.
[ 195 ]
Página 221
Abra seu navegador novamente e clique em um usuário que adicionou algumas imagens aos favoritos.
Você verá os detalhes do perfil, como segue:
https://translate.googleusercontent.com/translate_f 195/504
16/12/2021 10:51 Sem título
@ajax_required
@require_POST
@login_required
def user_follow (solicitação):
user_id = request.POST.get ('id')
action = request.POST.get ('ação')
se user_id e ação:
experimentar:
user = User.objects.get (id = user_id)
se ação == 'seguir':
Contact.objects.get_or_create (
[ 196 ]
Página 222
Capítulo 6
user_from = request.user,
user_to = user)
senão:
Contact.objects.filter (user_from = request.user,
user_to = user) .delete ()
return JsonResponse ({'status': 'ok'})
exceto User.DoesNotExist:
retornar JsonResponse ({'status': 'erro'})
retornar JsonResponse ({'status': 'erro'})
{% block domready%}
$ ('a.follow'). click (function (e) {
e.preventDefault ();
https://translate.googleusercontent.com/translate_f 196/504
16/12/2021 10:51 Sem título
$ .post ('{% url "user_follow"%}',
{
id: $ (this) .data ('id'),
ação: $ (this) .data ('ação')
},
função (dados) {
if (dados ['status'] == 'ok') {
var previous_action = $ ('a.follow'). data ('action');
// toggle data-action
$ ('a.follow'). data ('ação',
previous_action == 'seguir'? 'deixar de seguir': 'seguir');
// alternar texto do link
[ 197 ]
Página 223
$ ('a.follow'). text (
previous_action == 'seguir'? 'Deixar de seguir': 'Seguir');
Abra a página de detalhes do usuário de um usuário existente e clique no link SEGUIR para testar
a funcionalidade que você acabou de construir. Você verá que a contagem de seguidores aumentou:
https://translate.googleusercontent.com/translate_f 197/504
16/12/2021 10:51 Sem título
Você vai construir um aplicativo de fluxo de atividades para que cada usuário possa ver o
interações recentes dos usuários que eles seguem. Para fazer isso, você precisará de um modelo para salvar
as ações realizadas pelos usuários no site e uma maneira simples de adicionar ações para
a alimentação.
[ 198 ]
Página 224
Capítulo 6
Crie um novo aplicativo chamado Actions dentro do seu projeto com o seguinte
comando:
INSTALLED_APPS = [
# ...
'actions.apps.ActionsConfig',
]
classe Meta:
ordenação = ('-criado',)
O código anterior mostra o modelo de ação que será usado para armazenar atividades do usuário.
Os campos deste modelo são os seguintes:
• usuário : o usuário que executou a ação; esta é uma chave estrangeira para o Django
Modelo de usuário .
• verbo : o verbo que descreve a ação que o usuário executou.
• criado : a data e a hora em que esta ação foi criada. Você usa auto_
now_add = True para definir isso automaticamente para a data e hora atual quando o
objeto é salvo pela primeira vez no banco de dados.
Com este modelo básico, você só pode armazenar ações, como o usuário X fez algo . Você
precisa de um campo ForeignKey extra para salvar ações que envolvem um objeto de destino ,
tal como utilizador X marcada imagem Y ou utilizador X está agora a seguir utilizador Y . Como você já
sabe, uma ForeignKey normal pode apontar para apenas um modelo. Em vez disso, você precisará
uma forma de o objeto de destino da ação ser uma instância de um modelo existente. Isto é
o que o framework Django contenttypes o ajudará a fazer.
[ 199 ]
https://translate.googleusercontent.com/translate_f 198/504
16/12/2021 10:51 Sem título
Página 225
Vamos dar uma olhada em como você pode interagir com objetos ContentType . Abra a concha
usando o comando shell python manage.py . Você pode obter o ContentType
objeto correspondente a um modelo específico executando uma consulta com o app_label
e atributos do modelo , como segue:
>>> tipo_de_imagem
<ContentType: imagens | imagem>
Você também pode recuperar a classe de modelo de um objeto ContentType chamando seu
Método model_class () :
>>> image_type.model_class ()
<classe 'images.models.Image'>
[ 200 ]
https://translate.googleusercontent.com/translate_f 199/504
16/12/2021 10:51 Sem título
Página 226
Capítulo 6
Também é comum obter o objeto ContentType para uma classe de modelo específica,
do seguinte modo:
Estes são apenas alguns exemplos de uso de contenttypes . Django oferece mais maneiras de
trabalhar com eles. Você pode encontrar a documentação oficial sobre os tipos de conteúdo
quadro em https://docs.djangoproject.com/en/3.0/ref/contrib/
contenttypes / .
Edite o arquivo models.py do aplicativo de ações e faça com que tenha a seguinte aparência:
[ 201 ]
Página 227
https://translate.googleusercontent.com/translate_f 200/504
16/12/2021 10:51 Sem título
db_index = True)
target = GenericForeignKey ('target_ct', 'target_id')
created = models.DateTimeField (auto_now_add = True,
db_index = True)
classe Meta:
ordenação = ('-criado',)
Django não cria nenhum campo no banco de dados para campos GenericForeignKey .
Os únicos campos mapeados para os campos do banco de dados são target_ct e target_id .
Ambos os campos têm atributos em branco = True e null = True , de modo que um objeto de destino é
não é necessário ao salvar objetos de ação .
Execute o seguinte comando para criar migrações iniciais para este aplicativo:
Em seguida, execute o próximo comando para sincronizar o aplicativo com o banco de dados:
[ 202 ]
Página 228
Capítulo 6
https://translate.googleusercontent.com/translate_f 201/504
16/12/2021 10:51 Sem título
de django.contrib import admin
from .models import Action
@ admin.register (Ação)
classe ActionAdmin (admin.ModelAdmin):
list_display = ('usuário', 'verbo', 'destino', 'criado')
list_filter = ('criado',)
search_fields = ('verbo',)
[ 203 ]
Página 229
https://translate.googleusercontent.com/translate_f 202/504
16/12/2021 10:51 Sem título
A função create_action () permite que você crie ações que opcionalmente incluem
um objeto de destino . Você pode usar esta função em qualquer lugar do seu código como um atalho para
adicionar novas ações ao fluxo de atividades.
[ 204 ]
Página 230
Capítulo 6
https://translate.googleusercontent.com/translate_f 203/504
16/12/2021 10:51 Sem título
• Você cria umVocê
minuto. objeto Action
retorna se se
True nenhuma açãoAction
um objeto idêntica
foi já existir
criado ouno último
False caso contrário.
new_item.save ()
create_action (request.user, 'bookmarked image', new_item)
image.users_like.add (request.user)
create_action (request.user, 'likes', image)
[ 205 ]
Página 231
Como você pode ver no código anterior, graças ao seu modelo de ação e seu ajudante
função, é muito fácil salvar novas ações no fluxo de atividades.
https://translate.googleusercontent.com/translate_f 204/504
16/12/2021 10:51 Sem título
from actions.models import Action
@login_required
painel def (solicitação):
# Exibir todas as ações por padrão
ações = Action.objects.exclude (user = request.user)
following_ids = request.user.following.values_list ('id',
plano = verdadeiro)
se a seguir_ids:
# Se o usuário estiver seguindo outros, recupere apenas suas ações
actions = actions.filter (user_id__in = following_ids)
ações = ações [: 10]
[ 206 ]
Página 232
Capítulo 6
Usando select_related ()
Django oferece um método QuerySet chamado select_related () que permite a você
recuperar objetos relacionados para relacionamentos um-para-muitos. Isso se traduz em um único,
QuerySet mais complexo, mas você evita consultas adicionais ao acessar o
objetos relacionados. O método select_related é para ForeignKey e OneToOne
Campos. Funciona executando um SQL JOIN e incluindo os campos do relacionado
objeto na instrução SELECT .
https://translate.googleusercontent.com/translate_f 205/504
16/12/2021 10:51 Sem título
Além disso, adicione select_related aos campos que você usará, como este:
Use user__profile para unir a tabela Perfil em uma única consulta SQL. Se você ligar
select_related () sem passar nenhum argumento para ele, ele irá recuperar objetos de
todos os relacionamentos ForeignKey . Sempre limite select_related () aos relacionamentos
que será acessado posteriormente.
[ 207 ]
Página 233
Usando prefetch_related ()
select_related () irá ajudá-lo a aumentar o desempenho para recuperar objetos relacionados
em relacionamentos um-para-muitos. No entanto, select_related () não funciona para muitos-
relacionamentos para muitos ou muitos para um ( campos ManyToMany ou ForeignKey reversa ).
Django oferece um método QuerySet diferente chamado prefetch_related que funciona
para relacionamentos muitos para muitos e muitos para um, além dos relacionamentos
suportado por select_related () . O método prefetch_related () executa
uma pesquisa separada para cada relacionamento e junta os resultados usando Python. Isto
método também suporta a pré-busca de GenericRelation e GenericForeignKey .
Esta consulta agora está otimizada para recuperar as ações do usuário, incluindo objetos relacionados.
ações/
açao/
detail.html
{% load thumbnail%}
https://translate.googleusercontent.com/translate_f 206/504
16/12/2021 10:51 Sem título
<a href="{{ user.get_absolute_url }}">
<img src = "{{im.url}}" alt = "{{user.get_full_name}}"
class = "item-img">
</a>
{% fim se %}
[ 208 ]
Página 234
Capítulo 6
{% if action.target%}
{% with target = action.target%}
{% if target.image%}
{% thumbnail target.image "80x80" crop = "100%" como im%}
<a href="{{ target.get_absolute_url }}">
<img src = "{{im.url}}" class = "item-img">
</a>
{% fim se %}
{% endwith%}
{% fim se %}
</div>
<div class = "info">
p
<span class = "date"> {{action.created | timesince}} atrás </span>
<br />
<a href="{{ user.get_absolute_url }}">
{{user.first_name}}
</a>
{{ verbo de ação }}
{% if action.target%}
{% with target = action.target%}
<a href="{{ target.get_absolute_url }}"> {{target}} </a>
{% endwith%}
{% fim se %}
</p>
</div>
</div>
{% endwith%}
Este é o modelo usado para exibir um objeto Action . Primeiro, você usa o {% com
%} template tag para recuperar o usuário que está realizando a ação e o Perfil relacionado
objeto. Em seguida, você exibe a imagem do objeto de destino se o objeto de ação tiver
um objeto de destino relacionado . Por fim, você exibe o link para o usuário que executou
a ação, o verbo e o objeto de destino , se houver.
[ 209 ]
https://translate.googleusercontent.com/translate_f 207/504
16/12/2021 10:51 Sem título
Página 235
Você acabou de criar um fluxo de atividades completo para seus usuários e pode adicionar facilmente
novas ações do usuário para ele. Você também pode adicionar a funcionalidade de rolagem infinita à atividade
stream implementando o mesmo paginador AJAX que você usou para o image_
visualização de lista .
[ 210 ]
https://translate.googleusercontent.com/translate_f 208/504
16/12/2021 10:51 Sem título
Página 236
Capítulo 6
Vamos dar uma olhada em um exemplo de como melhorar suas consultas desnormalizando
contagens. Você vai desnormalizar dados do seu Imagem modelo e usar sinais Django
para manter os dados atualizados.
Estes são apenas um subconjunto dos sinais fornecidos pelo Django. Você pode encontrar uma lista de todos
sinais integrados em https://docs.djangoproject.com/en/3.0/ref/signals/ .
Digamos que você queira recuperar imagens por popularidade. Você pode usar o Django
funções de agregação para recuperar imagens ordenadas pelo número de usuários que gostam
eles. Lembre-se de que você usou funções de agregação do Django no Capítulo 3 , Estendendo
Seu aplicativo de blog . O código a seguir irá recuperar imagens de acordo com seus
número de curtidas:
images_by_popularity = Image.objects.annotate (
total_likes = Count ('users_like')). order_by ('- total_likes')
[ 211 ]
Página 237
https://translate.googleusercontent.com/translate_f 209/504
16/12/2021 10:51 Sem título
O campo total_likes permitirá que você armazene a contagem total de usuários que gostam de cada
imagem. Desnormalizar contagens é útil quando você deseja filtrar ou solicitar QuerySets
por eles.
Execute o seguinte comando para criar as migrações para adicionar o novo campo ao
tabela de banco de dados:
Você precisa conectar uma função de receptor ao sinal m2m_changed . Crie um novo arquivo
dentro do diretório do aplicativo de imagens e nomeie-o sinais.py . Adicione o seguinte
código para ele:
[ 212 ]
Página 238
Capítulo 6
instance.total_likes = instance.users_like.count ()
instance.save ()
https://translate.googleusercontent.com/translate_f 210/504
16/12/2021 10:51 Sem título
Primeiro, você registra a função users_like_changed como uma função receptora usando o
receptor () decorador. Você o conecta ao sinal m2m_changed . Então, você conecta
a função para Image.users_like.through para que a função só seja chamada se o
O sinal m2m_changed foi lançado por este remetente. Existe um método alternativo
para registrar uma função de receptor; consiste em usar o método connect () do
Objeto de sinal .
Você tem que conectar a função do receptor a um sinal para que seja chamado a cada
hora em que o sinal é enviado. O método recomendado para registrar seus sinais é
importando-os no método ready () da classe de configuração de seu aplicativo.
Django fornece um registro de aplicativo que permite que você configure e faça uma introspecção
seus aplicativos.
[ 213 ]
Página 239
nome = 'imagens'
Você importa os sinais para esta aplicação no método ready () para que eles sejam
importado quando o aplicativo de imagens é carregado.
https://translate.googleusercontent.com/translate_f 211/504
16/12/2021 10:51 Sem título
python manage.py runserver
Abra seu navegador para visualizar uma página de detalhes da imagem e clique no botão CURTIR .
Volte para o site de administração, navegue até a URL de edição da imagem, como
http://127.0.0.1:8000/admin/images/image/1/change/ e dê uma olhada
no atributo total_likes . Você deve ver que o atributo total_likes é
atualizado com o número total de usuários que gostam da imagem, da seguinte forma:
Figura 6.6: A página de edição de imagem no site de administração, incluindo desnormalização para curtidas totais
Agora, você pode usar o atributo total_likes para ordenar as imagens por popularidade
ou exiba o valor em qualquer lugar, evitando o uso de consultas complexas para calculá-lo.
Considere a seguinte consulta para obter imagens ordenadas de acordo com a contagem de curtidas:
images_by_popularity = Image.objects.annotate (
likes = Count ('users_like')). order_by ('- likes')
[ 214 ]
Página 240
Capítulo 6
Isso resulta em uma consulta SQL menos dispendiosa. Este é apenas um exemplo de como usar
Sinais do Django.
Você precisará definir as contagens iniciais para o resto da Imagem objetos para coincidir com o
estado atual do banco de dados. Abra o shell com o shell python manage.py
comando e execute o seguinte código:
https://translate.googleusercontent.com/translate_f 212/504
16/12/2021 10:51 Sem título
A contagem de curtidas para cada imagem agora está atualizada.
Embora SQL seja mais adequado para armazenamento de dados persistentes definidos por esquema, o Redis oferece
inúmeras vantagens ao lidar com dados que mudam rapidamente, armazenamento volátil,
ou quando um cache rápido é necessário. Vamos dar uma olhada em como o Redis pode ser usado para construir
uma nova funcionalidade em seu projeto.
Instalando Redis
Se você estiver usando Linux ou macOS, baixe a versão mais recente do Redis em https: //
redis.io/download . Descompacte o arquivo tar.gz , entre no diretório redis e compile
Redis usando o comando make , da seguinte maneira:
cd redis-5.0.8
faça
[ 215 ]
Página 241
O Redis agora está instalado em sua máquina. Se você estiver usando o Windows, o preferido
método para instalar o Redis é habilitar o subsistema Windows para Linux ( WSL )
e instale-o no sistema Linux. Você pode ler as instruções sobre como habilitar WSL
e instalando o Redis em https://redislabs.com/blog/redis-on-windows-10/ .
Depois de instalar o Redis, use o seguinte comando shell para iniciar o servidor Redis:
src / redis-server
Você deve ver uma saída que termina com as seguintes linhas:
# Servidor inicializado
* Pronto para aceitar conexões
Por padrão, o Redis é executado na porta 6379 . Você pode especificar uma porta personalizada usando o --port
sinalizador, por exemplo, redis-server --port 6655 .
Mantenha o servidor Redis em execução e abra outro shell. Inicie o cliente Redis com
o seguinte comando:
src / redis-cli
127.0.0.1:6379>
O cliente Redis permite que você execute comandos do Redis diretamente do shell. Vamos
https://translate.googleusercontent.com/translate_f 213/504
16/12/2021 10:51 Sem título
tente alguns comandos. Insira o comando SET no shell do Redis para armazenar um valor em um
chave:
OK
O comando anterior cria uma chave de nome com o valor da string "Peter" no
Banco de dados Redis. A saída OK indica que a chave foi salva com sucesso.
"Pedro"
Você também pode verificar se existe uma chave usando o comando EXISTS . Este comando
retorna 1 se a chave fornecida existe e 0 caso contrário:
(inteiro) 1
[ 216 ]
Página 242
Capítulo 6
Você pode definir o tempo para uma chave expirar usando o comando EXPIRE , que permite
você pode definir o tempo de vida em segundos. Outra opção é usar o comando EXPIREAT ,
que espera um carimbo de data / hora Unix. A expiração da chave é útil para usar o Redis como cache
ou para armazenar dados voláteis:
A resposta (nula) é uma resposta nula e significa que nenhuma chave foi encontrada. Você
também pode excluir qualquer chave usando o comando DEL , da seguinte maneira:
Estes são apenas comandos básicos para operações de teclas. Você pode dar uma olhada em todos os Redis
comandos em https://redis.io/commands e todos os tipos de dados Redis em https: //
redis.io/topics/data-types .
[ 217 ]
Página 243
O código anterior cria uma conexão com o banco de dados Redis. No Redis, bancos de dados
são identificados por um índice inteiro em vez de um nome de banco de dados. Por padrão, um cliente é
conectado ao banco de dados 0 . O número de bancos de dados Redis disponíveis é definido como 16 ,
mas você pode mudar isso no arquivo de configuração redis.conf .
O comando retorna True , indicando que a chave foi criada com sucesso.
Agora você pode recuperar a chave usando o comando get () :
b'bar '
Vamos integrar o Redis ao seu projeto. Edite o arquivo settings.py dos favoritos
projeto e adicione as seguintes configurações a ele:
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 0
Estas são as configurações do servidor Redis e do banco de dados que você usará para
seu projecto.
https://translate.googleusercontent.com/translate_f 215/504
16/12/2021 10:51 Sem título
[ 218 ]
Página 244
Capítulo 6
importar redis
das configurações de importação do django.conf
# conectar ao redis
r = redis.Redis (host = settings.REDIS_HOST,
port = settings.REDIS_PORT,
db = settings.REDIS_DB)
Nesta visão, você usa o comando incr que incrementa o valor de uma determinada chave por
1 . Se a chave não existir, o comando incr a criará. O método incr () retorna
o valor final da chave após realizar a operação. Você armazena o valor no
variável total_views e passá-la no contexto do template. Você constrói a chave Redis
usando uma notação, como object-type: id: field (por exemplo, image: 33: id ).
A convenção para nomear chaves Redis é usar um sinal de dois pontos como
um separador para a criação de chaves com espaço de nomes. Ao fazer isso, a chave
nomes são especialmente verbosos e as chaves relacionadas compartilham parte do
mesmo esquema em seus nomes.
[ 219 ]
https://translate.googleusercontent.com/translate_f 216/504
16/12/2021 10:51 Sem título
Página 245
Agora, abra uma página de detalhes da imagem em seu navegador e recarregue-a várias vezes.
Você verá que cada vez que a visualização é processada, o total de visualizações exibidas é
incrementado em 1. Dê uma olhada no seguinte exemplo:
Excelente! Você integrou com êxito o Redis ao seu projeto para armazenar contagens de itens.
[ 220 ]
https://translate.googleusercontent.com/translate_f 217/504
16/12/2021 10:51 Sem título
Página 246
Capítulo 6
Você usa o comando zincrby () para armazenar visualizações de imagens em um conjunto classificado com o
imagem: chave de classificação . Você armazenará o id da imagem e uma pontuação relacionada de 1 , que
será adicionado à pontuação total deste elemento no conjunto classificado. Isso vai permitir que você
para manter o controle de todas as visualizações de imagens globalmente e ter um conjunto classificado ordenado pelo total
número de visualizações.
Agora, crie uma nova visualização para exibir a classificação das imagens mais visualizadas. Adicione o
o seguinte código para o arquivo views.py do aplicativo de imagens :
@login_required
def image_ranking (solicitação):
# obter dicionário de classificação de imagens
image_ranking = r.zrange ('image_ranking', 0, -1,
desc = True) [: 10]
image_ranking_ids = [int (id) para id em image_ranking]
# obter imagens mais vistas
most_viewed = list (Image.objects.filter (
id__in = image_ranking_ids))
most_viewed.sort (key = lambda x: image_ranking_ids.index (x.id))
retornar render (pedido,
'images / image / ranking.html',
{'seção': 'imagens',
'most_viewed': most_viewed})
[ 221 ]
Página 247
https://translate.googleusercontent.com/translate_f 218/504
16/12/2021 10:51 Sem título
{% extends "base.html"%}
{% block content%}
<h1> Classificação de imagens </h1>
<ol>
{% para a imagem em most_viewed%}
<li>
<a href="{{ image.get_absolute_url }}">
{{image.title}}
</a>
</li>
{% endfor%}
</ol>
{% endblock%}
Finalmente, você precisa criar um padrão de URL para a nova visualização. Edite o arquivo urls.py
do aplicativo de imagens e adicione o seguinte padrão a ele:
[ 222 ]
Página 248
Capítulo 6
https://translate.googleusercontent.com/translate_f 219/504
16/12/2021 10:51 Sem título
• Contagem : como você viu, é muito fácil gerenciar contadores com o Redis.
Você pode usar incr () e incrby () para contar coisas.
• Armazenando os itens mais recentes : Você pode adicionar itens ao início / final de uma lista usando
lpush () e rpush () . Remova e retorne o primeiro / último elemento usando
lpop () / rpop () . Você pode cortar o comprimento da lista usando ltrim () para manter
seu comprimento.
• Filas : além dos comandos push e pop , o Redis oferece o bloqueio
de comandos de fila.
• Cache : usar expire () e expireat () permite que você use o Redis como
um cache. Você também pode encontrar back-ends de cache Redis de terceiros para Django.
• Pub / sub : o Redis fornece comandos para assinatura / cancelamento de assinatura e
envio de mensagens para canais.
• Classificações e tabelas de classificação : os conjuntos classificados do Redis com pontuações tornam isso muito fácil
para criar tabelas de classificação.
• Rastreamento em tempo real : o I / O rápido do Redis o torna perfeito para cenários em tempo real.
Resumo
Neste capítulo, você construiu um sistema a seguir usando relacionamentos muitos-para-muitos com um
modelo intermediário. Você também criou um fluxo de atividade usando relações genéricas e
você otimizou QuerySets para recuperar objetos relacionados. Este capítulo então introduziu
você aos sinais do Django, e você criou uma função de receptor de sinal para desnormalizar
contagens de objetos relacionados. Cobrimos as classes de configuração do aplicativo, que você usou
para carregar seus manipuladores de sinal. Você também aprendeu como instalar e configurar o Redis
em seu projeto Django. Por fim, você usou o Redis em seu projeto para armazenar visualizações de itens,
e você construiu uma classificação de imagem com o Redis.
No próximo capítulo, você aprenderá como construir uma loja online. Você vai criar
um catálogo de produtos e construir um carrinho de compras usando sessões. Você também vai descobrir
como iniciar tarefas assíncronas usando Celery.
[ 223 ]
Página 250
249
https://translate.googleusercontent.com/translate_f 220/504
16/12/2021 10:51 Sem título
[ 225 ]
Página 251
• Construir um sistema de carrinho de compras usando sessões Django para permitir que os usuários mantenham
produtos selecionados enquanto navegam no site
• Criação do formulário e funcionalidade para fazer pedidos no site
• Enviar uma confirmação de e-mail assíncrona para os usuários quando eles colocam
uma ordem
Abra um shell, crie um ambiente virtual para o novo projeto e ative-o com
https://translate.googleusercontent.com/translate_f 221/504
16/12/2021 10:51 Sem título
os seguintes comandos:
mkdir env
Inicie um novo projeto chamado myshop com um aplicativo chamado shop abrindo um shell
e executando os seguintes comandos:
cd myshop /
INSTALLED_APPS = [
# ...
'shop.apps.ShopConfig',
]
Seu aplicativo agora está ativo para este projeto. Vamos definir os modelos para o
catálogo de produtos.
[ 226 ]
Página 252
Capítulo 7
Edite o arquivo models.py do aplicativo da loja que você acabou de criar e adicione o
seguinte código:
classe Meta:
ordenação = ('nome',)
verbose_name = 'categoria'
verbose_name_plural = 'categorias'
https://translate.googleusercontent.com/translate_f 222/504
16/12/2021 10:51 Sem título
classe Produto (modelos.Modelo):
categoria = modelos.ForeignKey (categoria,
related_name = 'produtos',
on_delete = models.CASCADE)
name = models.CharField (max_length = 200, db_index = True)
slug = models.SlugField (max_length = 200, db_index = True)
image = models.ImageField (upload_to = 'products /% Y /% m /% d',
em branco = verdadeiro)
descrição = models.TextField (em branco = True)
preço = modelos.DecimalField (max_digits = 10, decimal_places = 2)
available = models.BooleanField (default = True)
created = models.DateTimeField (auto_now_add = True)
updated = models.DateTimeField (auto_now = True)
classe Meta:
ordenação = ('nome',)
index_together = (('id', 'slug'),)
[ 227 ]
Página 253
• categoria : Uma chave estrangeira para o modelo de categoria . Este é um para muitos
relacionamento: um produto pertence a uma categoria e uma categoria contém
vários produtos.
• nome : o nome do produto.
• slug : o slug deste produto para construir URLs bonitos.
Para o campo de preço , você usa DecimalField em vez de FloatField para evitar
questões de arredondamento.
https://translate.googleusercontent.com/translate_f 223/504
16/12/2021 10:51 Sem título
Na classe Meta do modelo de produto , você usa a meta opção index_together
para especificar um índice para os campos id e slug juntos. Você define este índice porque
você planeja consultar produtos por id e slug . Ambos os campos são indexados juntos
para melhorar o desempenho de consultas que utilizam os dois campos.
Já que você vai lidar com imagens em seus modelos, abra o shell e instale
Almofada com o seguinte comando:
Agora execute o próximo comando para criar migrações iniciais para seu projeto:
[ 228 ]
Página 254
Capítulo 7
@ admin.register (categoria)
classe CategoryAdmin (admin.ModelAdmin):
list_display = ['nome', 'slug']
prepopulated_fields = {'slug': ('nome',)}
@ admin.register (Produto)
classe ProductAdmin (admin.ModelAdmin):
list_display = ['nome', 'slug', 'preço',
'disponível', 'criado', 'atualizado']
list_filter = ['disponível', 'criado', 'atualizado']
list_editable = ['preço', 'disponível']
prepopulated_fields = {'slug': ('nome',)}
https://translate.googleusercontent.com/translate_f 224/504
16/12/2021 10:51 Sem título
Lembre-se de que você usa o atributo prepopulated_fields para especificar campos onde
o valor é definido automaticamente usando o valor de outros campos. Como você viu
antes, isso é conveniente para gerar lesmas.
[ 229 ]
Página 255
Você usa o atributo list_editable na classe ProductAdmin para definir os campos que
pode ser editado na página de exibição de lista do site de administração. Isso vai permitir
você a editar várias linhas de uma vez. Qualquer campo em list_editable também deve ser listado
no atributo list_display , uma vez que apenas os campos exibidos podem ser editados.
[ 230 ]
https://translate.googleusercontent.com/translate_f 225/504
16/12/2021 10:51 Sem título
Página 256
Capítulo 7
No código anterior, você filtra o QuerySet com available = True para recuperar apenas
produtos disponíveis. Você usa um parâmetro opcional category_slug para, opcionalmente,
filtrar produtos por uma determinada categoria.
Você também precisa de uma visualização para recuperar e exibir um único produto. Adicione o seguinte
visualizar o arquivo views.py :
Depois de construir a lista de produtos e visualizações de detalhes, você deve definir padrões de URL
para eles. Crie um novo arquivo dentro do diretório do aplicativo da loja e nomeie-o como urls.
py . Adicione o seguinte código a ele:
app_name = 'loja'
urlpatterns = [
caminho ('', views.product_list, name = 'product_list'),
[ 231 ]
https://translate.googleusercontent.com/translate_f 226/504
16/12/2021 10:51 Sem título
Página 257
Estes são os padrões de URL para seu catálogo de produtos. Você definiu dois
diferentes padrões de URL para a visualização product_list : um padrão denominado product_list ,
que chama a visualização product_list sem quaisquer parâmetros e um padrão denominado
product_list_by_category , que fornece um parâmetro category_slug para o
visualização para filtrar produtos de acordo com uma determinada categoria. Você adicionou um padrão para
a visão product_detail , que passa os parâmetros id e slug para a visão
para recuperar um produto específico.
Edite o arquivo urls.py do projeto myshop para torná-lo parecido com este:
urlpatterns = [
caminho ('admin /', admin.site.urls),
path ('', include ('shop.urls', namespace = 'shop')),
]
Nos principais padrões de URL do projeto, você inclui URLs para o aplicativo da loja
em um namespace personalizado denominado shop .
[ 232 ]
Página 258
https://translate.googleusercontent.com/translate_f 227/504
16/12/2021 10:51 Sem título
Capítulo 7
modelos/
fazer compras/
base.html
produtos/
list.html
detail.html
Você precisa definir um modelo básico e, em seguida, estendê-lo na lista de produtos e detalhes
modelos. Edite o modelo shop / base.html e adicione o seguinte código a ele:
{% load static%}
<! DOCTYPE html>
<html>
<head>
<meta charset = "utf-8" />
<title> {% block title%} Minha loja {% endblock%} </title>
<link href = "{% static" css / base.css "%}" rel = "stylesheet">
</head>
<body>
<div id = "header">
<a href="/" class="logo"> Minha loja </a>
</div>
<div id = "subheader">
<div class = "cart">
Seu carrinho está vazio.
</div>
</div>
<div id = "content">
{% block content%}
{% endblock%}
</div>
</body>
</html>
[ 233 ]
Página 259
Este é o modelo básico que você usará em sua loja. Para incluir o
Estilos CSS e imagens que são usados pelos modelos, você precisa copiar o estático
https://translate.googleusercontent.com/translate_f 228/504
16/12/2021 10:51 Sem título
arquivos que acompanham este capítulo, que estão localizados no diretório estático / do
aplicativo de loja . Copie-os para o mesmo local em seu projeto. Você pode encontrar o
conteúdo do diretório em https://github.com/PacktPublishing/Django-3-by-
Exemplo / tree / master / Chapter07 / myshop / shop / static .
{% block title%}
{% if category%} {{category.name}} {% else%} Produtos {% endif%}
{% endblock%}
{% block content%}
<div id = "sidebar">
<h3> Categorias </h3>
<ul>
<li {% if not category%} class = "selecionado" {% endif%}>
<a href="{% url "shop:product_list" %}"> Todos </a>
</li>
{% para c nas categorias%}
<li {% if category.slug == c.slug%} class = "selecionado"
{% endif%}>
<a href="{{ c.get_absolute_url }}"> {{c.name}} </a>
</li>
{% endfor%}
</ul>
</div>
<div id = "main" class = "product-list">
<h1> {% if category%} {{category.name}} {% else%} Products
{% endif%} </h1>
{% para produto em% de produtos}
<div class = "item">
<a href="{{ product.get_absolute_url }}">
<img src = "{% if product.image%} {{product.image.url}} {%
else%} {% static "img / no_image.png"%} {% endif%} ">
</a>
<a href="{{ product.get_absolute_url }}"> {{product.name}} </
a>
<br>
$ {{product.price}}
</div>
[ 234 ]
Página 260
Capítulo 7
{% endfor%}
</div>
{% endblock%}
https://translate.googleusercontent.com/translate_f 229/504
16/12/2021 10:51 Sem título
o campo de imagem do modelo do produto pode estar em branco, você precisa fornecer um padrão
imagem para os produtos que não têm imagem. A imagem está localizada em seu estático
diretório de arquivos com o caminho relativo img / no_image.png .
Uma vez que você está usando ImageField para armazenar imagens de produtos, você precisa do desenvolvimento
servidor para servir arquivos de imagem carregados.
MEDIA_URL é o URL base que serve os arquivos de mídia carregados pelos usuários. MEDIA_ROOT é
o caminho local onde esses arquivos residem, que você constrói prefixando dinamicamente
a variável BASE_DIR .
Para que o Django sirva os arquivos de mídia carregados usando o servidor de desenvolvimento, edite o
arquivo urls.py principal de myshop e adicione o seguinte código a ele:
urlpatterns = [
# ...
]
if settings.DEBUG:
urlpatterns + = static (settings.MEDIA_URL,
document_root = settings.MEDIA_ROOT)
Lembre-se de que você só serve arquivos estáticos dessa maneira durante o desenvolvimento. Dentro
um ambiente de produção, você nunca deve servir arquivos estáticos com Django; a
O servidor de desenvolvimento Django não serve arquivos estáticos de maneira eficiente. Capítulo
14 , Going Live , irá ensiná-lo a servir arquivos estáticos em um ambiente de produção.
[ 235 ]
Página 261
https://translate.googleusercontent.com/translate_f 230/504
16/12/2021 10:51 Sem título
Se você criar um produto usando o site de administração e não carregar nenhuma imagem para
nele, a imagem padrão no_image.png será exibida em seu lugar:
Figura 7.3: A lista de produtos exibindo uma imagem padrão para produtos que não possuem imagem
{% block title%}
{{ Nome do Produto }}
{% endblock%}
{% block content%}
<div class = "product-detail">
[ 236 ]
Página 262
Capítulo 7
https://translate.googleusercontent.com/translate_f 231/504
16/12/2021 10:51 Sem título
[ 237 ]
Página 263
Você usará a estrutura de sessão do Django para persistir o carrinho. O carrinho será mantido em
a sessão até que ela termine ou até que o usuário faça o check-out do carrinho. Você também precisará
construir modelos Django adicionais para o carrinho e seus itens.
Para usar as sessões, você deve se certificar de que a configuração MIDDLEWARE do seu projeto
contém 'django.contrib.sessions.middleware.SessionMiddleware' . Isto
middleware gerencia sessões. É adicionado por padrão à configuração MIDDLEWARE
ao criar um novo projeto usando o comando startproject .
request.session.get ('foo')
Exclua uma chave que você armazenou anteriormente na sessão da seguinte maneira:
https://translate.googleusercontent.com/translate_f 232/504
16/12/2021 10:51 Sem título
[ 238 ]
Página 264
Capítulo 7
Configurações de sessão
Existem várias configurações que você pode usar para configurar sessões para seu projeto. O
o mais importante é SESSION_ENGINE . Esta configuração permite que você defina o lugar onde
as sessões são armazenadas. Por padrão, Django armazena sessões no banco de dados usando o
Modelo de sessão do aplicativo django.contrib.sessions .
• Sessões do banco de dados : os dados da sessão são armazenados no banco de dados. Este é o padrão
mecanismo de sessão.
• Sessões baseadas em arquivo : os dados da sessão são armazenados no sistema de arquivos.
• Sessões em cache : os dados da sessão são armazenados em um backend de cache. Você pode especificar
backends de cache usando a configuração CACHES . Armazenamento de dados da sessão em um cache
sistema oferece o melhor desempenho.
• Sessões de banco de dados em cache : os dados da sessão são armazenados em um cache de gravação
e banco de dados. As leituras só usam o banco de dados se os dados ainda não estiverem no
cache.
• Sessões baseadas em cookies: os dados da sessão são armazenados nos cookies que são enviados
para o navegador.
Você pode personalizar as sessões com configurações específicas. Aqui estão alguns dos importantes
configurações relacionadas à sessão:
https://translate.googleusercontent.com/translate_f 233/504
16/12/2021 10:51 Sem título
• SESSION_EXPIRE_AT_BROWSER_CLOSE : um booleano indicando que a sessão
tem que expirar quando o navegador é fechado.
• SESSION_SAVE_EVERY_REQUEST : um booleano que, se verdadeiro , salvará a sessão
para o banco de dados em cada solicitação. A expiração da sessão também é atualizada a cada
tempo é salvo.
[ 239 ]
Página 265
Você pode ver todas as configurações da sessão e seus valores padrão em https: // docs.
djangoproject.com/en/3.0/ref/settings/#sessions.
Expiração da sessão
Você pode escolher usar sessões de comprimento de navegador ou sessões persistentes usando
a configuração SESSION_EXPIRE_AT_BROWSER_CLOSE . Isso é definido como False por padrão,
forçando a duração da sessão para o valor armazenado na configuração SESSION_COOKIE_AGE .
Se você definir SESSION_EXPIRE_AT_BROWSER_CLOSE como True , a sessão irá expirar
quando o usuário fecha o navegador, e a configuração SESSION_COOKIE_AGE não
tem qualquer efeito.
Uma vez que os preços dos produtos podem variar, vamos usar a abordagem de armazenar o preço do produto
junto com o próprio produto quando adicionado ao carrinho. Ao fazer isso, você usa o
preço atual do produto quando os usuários o adicionam ao carrinho, não importa se o
o preço do produto é alterado posteriormente. Isso significa que o preço que o item tem
quando o cliente adiciona ao carrinho é mantido para esse cliente na sessão até
o checkout é concluído ou a sessão termina.
Em seguida, você deve construir uma funcionalidade para criar carrinhos de compras e associá-los
com sessões. Isso deve funcionar da seguinte maneira:
• Quando um carrinho é necessário, você verifica se uma chave de sessão personalizada está definida. Se
nenhum carrinho é definido na sessão, você cria um novo carrinho e salva-o no carrinho
chave de sessão.
• Para solicitações sucessivas, você realiza a mesma verificação e obtém os itens do carrinho
da chave de sessão do carrinho. Você recupera os itens do carrinho da sessão e
seus objetos Product relacionados do banco de dados.
[ 240 ]
https://translate.googleusercontent.com/translate_f 234/504
16/12/2021 10:51 Sem título
Página 266
Capítulo 7
CART_SESSION_ID = 'carrinho'
Esta é a chave que você usará para armazenar o carrinho na sessão do usuário. Desde a
As sessões do Django são gerenciadas por visitante, você pode usar a mesma chave de sessão do carrinho para
todas as sessões.
INSTALLED_APPS = [
# ...
'shop.apps.ShopConfig',
'cart.apps.CartConfig',
]
Criar um novo arquivo dentro do carrinho de diretório de aplicativos e nomeá-la cart.py . Adicione o
seguinte código para ele:
Esta é a classe Cart que permitirá a você gerenciar o carrinho de compras. Voce requere
o carrinho a ser inicializado com um objeto de solicitação . Você armazena a sessão atual usando
self.session = request.session para torná-lo acessível aos outros métodos do
Classe do carrinho .
[ 241 ]
https://translate.googleusercontent.com/translate_f 235/504
16/12/2021 10:51 Sem título
Página 267
Você construirá seu dicionário de carrinho com IDs de produto como chaves, e para cada produto
chave, um dicionário será um valor que inclui quantidade e preço. Fazendo isso,
você pode garantir que um produto não será adicionado mais de uma vez ao carrinho.
Dessa forma, você também pode simplificar a recuperação de itens do carrinho.
Vamos criar um método para adicionar produtos ao carrinho ou atualizar sua quantidade. Adicione o
seguintes métodos add () e save () para a classe Cart :
Você usa a ID do produto como uma chave no dicionário de conteúdo do carrinho. Você converte o
ID do produto em uma string porque o Django usa JSON para serializar os dados da sessão, e
JSON permite apenas nomes de chave de string. O ID do produto é a chave e o valor
que você persiste é um dicionário com valores de quantidade e preço do produto.
O preço do produto é convertido de decimal em string para serializar
isto. Finalmente, você chama o método save () para salvar o carrinho na sessão.
[ 242 ]
Página 268
https://translate.googleusercontent.com/translate_f 236/504
16/12/2021 10:51 Sem título
Capítulo 7
Você também precisa de um método para remover produtos do carrinho. Adicione o seguinte
método para a classe Cart :
Você terá que iterar pelos itens contidos no carrinho e acessar os itens relacionados
Instâncias do produto . Para fazer isso, você pode definir um método __iter __ () em sua classe.
Adicione o seguinte método à classe Cart :
cart = self.cart.copy ()
para produto em produtos:
cart [str (product.id)] ['product'] = produto
[ 243 ]
Página 269
https://translate.googleusercontent.com/translate_f 237/504
16/12/2021 10:51 Sem título
variável e adicionar as instâncias de Produto a ela. Finalmente, você itera sobre os itens do carrinho,
converter o preço de cada item de volta em decimal e adicionar um atributo total_price
para cada item. Este método __iter __ () permitirá que você itere facilmente sobre os itens
no carrinho em visualizações e modelos.
Você também precisa encontrar uma maneira de retornar o número total de itens no carrinho. Quando o len ()
função é executada em um objeto, Python chama seu método __len __ () para recuperar seu
comprimento. Em seguida, você vai definir um método personalizado __len __ () para retornar o total
número de itens armazenados no carrinho.
Adicione o seguinte método para calcular o custo total dos itens no carrinho:
Sua classe Cart agora está pronta para gerenciar carrinhos de compras.
[ 244 ]
Página 270
Capítulo 7
• Uma visão para adicionar ou atualizar itens no carrinho que pode lidar com novos e atuais
quantidades
• Uma visão para remover itens do carrinho
• Uma visão para exibir os itens e totais do carrinho
https://translate.googleusercontent.com/translate_f 238/504
16/12/2021 10:51 Sem título
Você usará este formulário para adicionar produtos ao carrinho. Sua classe CartAddProductForm
contém os dois campos a seguir:
• quantidade : permite que o usuário selecione uma quantidade entre um e 20. Você
use um campo TypedChoiceField com coerce = int para converter a entrada em
um inteiro.
• substituir : permite que você indique se a quantidade deve ser adicionada
a qualquer quantidade existente no carrinho para este produto ( Falso ), ou se o
a quantidade existente deve ser substituída pela quantidade fornecida ( Verdadeiro ). Você
use um widget HiddenInput para este campo, uma vez que você não deseja exibi-lo
para o usuário.
[ 245 ]
Página 271
Vamos criar uma visão para adicionar itens ao carrinho. Edite o arquivo views.py do carrinho
aplicativo e adicione o seguinte código a ele:
@require_POST
def cart_add (solicitação, product_id):
carrinho = carrinho (pedido)
product = get_object_or_404 (produto, id = product_id)
form = CartAddProductForm (request.POST)
if form.is_valid ():
cd = form.cleaned_data
cart.add (produto = produto,
https://translate.googleusercontent.com/translate_f 239/504
16/12/2021 10:51 Sem título
quantidade = cd ['quantidade'],
override_quantity = cd ['override'])
return redirect ('cart: cart_detail')
Você também precisa de uma visão para remover itens do carrinho. Adicione o seguinte código ao
arquivo views.py do aplicativo de carrinho :
@require_POST
def cart_remove (solicitação, product_id):
carrinho = carrinho (pedido)
product = get_object_or_404 (produto, id = product_id)
cart.remove (produto)
return redirect ('cart: cart_detail')
[ 246 ]
Página 272
Capítulo 7
Finalmente, você precisa de uma visão para exibir o carrinho e seus itens. Adicione a seguinte vista
ao arquivo views.py do aplicativo de carrinho :
Você criou visualizações para adicionar itens ao carrinho, atualizar quantidades, remover itens
do carrinho e exiba o conteúdo do carrinho. Vamos adicionar padrões de URL para essas visualizações.
Criar um novo arquivo dentro do carrinho de diretório de aplicativos e nomeá-la urls.py . Adicionar
os seguintes URLs para ele:
app_name = 'carrinho'
urlpatterns = [
caminho ('', views.cart_detail, name = 'cart_detail'),
caminho ('add / <int: product_id> /', views.cart_add, name = 'cart_add'),
caminho ('remove / <int: product_id> /', views.cart_remove,
nome = 'cart_remove'),
]
https://translate.googleusercontent.com/translate_f 240/504
16/12/2021 10:51 Sem título
Edite o arquivo urls.py principal do projeto myshop e adicione o seguinte padrão de URL
para incluir os URLs do carrinho:
urlpatterns = [
caminho ('admin /', admin.site.urls),
caminho ('cart /', include ('cart.urls', namespace = 'cart')),
path ('', include ('shop.urls', namespace = 'shop')),
]
Certifique-se de incluir este padrão de URL antes do padrão shop.urls , uma vez que
é mais restritivo do que o último.
modelos/
carrinho/
detail.html
[ 247 ]
Página 273
{% block title%}
Seu carrinho de compras
{% endblock%}
{% block content%}
<h1> Seu carrinho de compras </h1>
<table class = "cart">
<thead>
<tr>
<th> Imagem </th>
<th> Produto </th>
<th> Quantidade </th>
<th> Remover </th>
<th> Preço unitário </th>
<th> Preço </th>
</tr>
</thead>
<tbody>
{% para item no carrinho%}
{% com produto = item.produto%}
<tr>
<td>
<a href="{{ product.get_absolute_url }}">
<img src = "{% if product.image%} {{product.image.url
}}
{% else%} {% static "img / no_image.png"%} {% endif%} ">
</a>
</td>
https://translate.googleusercontent.com/translate_f 241/504
16/12/2021 10:51 Sem título
<td> {{product.name}} </td>
<td> {{item.quantity}} </td>
<td>
<form action = "{% url" cart: cart_remove "product.id%}"
method = "post">
<input type = "submit" value = "Remove">
{% csrf_token%}
</form>
</td>
<td class = "num"> $ {{item.price}} </td>
<td class = "num"> $ {{item.total_price}} </td>
</tr>
{% endwith%}
{% endfor%}
[ 248 ]
Página 274
Capítulo 7
Este é o modelo usado para exibir o conteúdo do carrinho. Contém uma mesa
com os itens armazenados no carrinho atual. Você permite que os usuários alterem a quantidade de
os produtos selecionados usando um formulário postado na visão cart_add . Você também
permitir que os usuários removam itens do carrinho, fornecendo um botão Remover para cada
deles. Finalmente, você usa um formulário HTML com um atributo de ação que aponta para o
cart_remove URL incluindo o ID do produto.
https://translate.googleusercontent.com/translate_f 242/504
16/12/2021 10:51 Sem título
[ 249 ]
Página 275
{{cart_product_form}}
{% csrf_token%}
<input type = "submit" value = "Add to cart">
</form>
{{product.description | quebras de linha}}
Escolha uma quantidade e clique no botão Adicionar ao carrinho . O formulário é enviado para
a visão cart_add via POST . A visão adiciona o produto ao carrinho na sessão,
incluindo seu preço atual e a quantidade selecionada. Em seguida, ele redireciona o usuário para o
página de detalhes do carrinho, que será semelhante à seguinte captura de tela:
[ 250 ]
https://translate.googleusercontent.com/translate_f 243/504
16/12/2021 10:51 Sem título
Página 276
Capítulo 7
Você cria uma instância de CartAddProductForm para cada item no carrinho para permitir
alterar as quantidades do produto. Você inicializa o formulário com a quantidade do item atual
e defina o campo de substituição como True para que, ao enviar o formulário para o cart_
adicionar visualização, a quantidade atual é substituída pela nova.
<td>
<form action = "{% url" cart: cart_add "product.id%}" method = "post">
{{item.update_quantity_form.quantity}}
{{item.update_quantity_form.override}}
<input type = "submit" value = "Update">
{% csrf_token%}
</form>
</td>
[ 251 ]
https://translate.googleusercontent.com/translate_f 244/504
16/12/2021 10:51 Sem título
Página 277
Você verá um formulário para editar a quantidade de cada item do carrinho, da seguinte maneira:
Figura 7.7: A página de detalhes do carrinho, incluindo o formulário para atualizar as quantidades do produto
Processadores de contexto
Um processador de contexto é uma função Python que leva o objeto de solicitação como um
argumento e retorna um dicionário que é adicionado ao contexto da solicitação. Contexto
processadores são úteis quando você precisa disponibilizar algo globalmente
para todos os modelos.
Por padrão, quando você cria um novo projeto usando o comando startproject ,
seu projeto contém os seguintes processadores de contexto de modelo no
opção context_processors dentro da configuração TEMPLATES :
[ 252 ]
Página 278
https://translate.googleusercontent.com/translate_f 245/504
16/12/2021 10:51 Sem título
Capítulo 7
Você pode ver a lista de todos os processadores de contexto integrados em https: // docs.
djangoproject.com/en/3.0/ref/templates/api/#built-in-template-
processadores de contexto.
Em seu processador de contexto, você instancia o carrinho usando o objeto de solicitação e faz
está disponível para os modelos como uma variável chamada carrinho .
MODELOS = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': Verdadeiro,
'OPÇÕES': {
[ 253 ]
Página 279
'context_processors': [
# ...
https://translate.googleusercontent.com/translate_f 246/504
16/12/2021 10:51 Sem título
'cart.context_processors.cart',
],
},
},
]
O processador de contexto do carrinho será executado toda vez que um modelo for renderizado
usando RequestContext do Django . A variável do carrinho será definida no contexto
de seus modelos. Você pode ler mais sobre RequestContext em https: //
docs.djangoproject.com/en/3.0/ref/templates/api/#django.template.
RequestContext.
[ 254 ]
Página 280
Capítulo 7
No cabeçalho do site, agora você pode ver o número total de itens no carrinho
e o custo total, da seguinte forma:
https://translate.googleusercontent.com/translate_f 247/504
16/12/2021 10:51 Sem título
INSTALLED_APPS = [
# ...
'orders.apps.OrdersConfig',
]
[ 255 ]
Página 281
classe Meta:
ordenação = ('-criado',)
https://translate.googleusercontent.com/translate_f 248/504
16/12/2021 10:51 Sem título
related_name = 'itens',
on_delete = models.CASCADE)
product = models.ForeignKey (Product,
related_name = 'order_items',
on_delete = models.CASCADE)
preço = modelos.DecimalField (max_digits = 10, decimal_places = 2)
quantidade = modelos.PositivoIntegerField (padrão = 1)
A Order modelo contém vários campos para armazenar informações do cliente e um pago
Campo booleano, cujo padrão é False . Posteriormente, você usará este campo para
diferencie entre pedidos pagos e não pagos. Você também define um get_total_cost ()
método para obter o custo total dos itens comprados neste pedido.
O modelo OrderItem permite que você armazene o produto, quantidade e preço pago por
cada item. Você inclui get_cost () para retornar o custo do item.
[ 256 ]
Página 282
Capítulo 7
Execute o próximo comando para criar migrações iniciais para o aplicativo de pedidos :
@ admin.register (pedido)
classe OrderAdmin (admin.ModelAdmin):
list_display = ['id', 'first_name', 'last_name', 'email',
'endereço', 'postal_code', 'cidade', 'pago',
'criado', 'atualizado']
list_filter = ['pago', 'criado', 'atualizado']
inlines = [OrderItemInline]
Você usa um ModelInline classe para o OrderItem modelo para incluí-lo como uma linha
na classe OrderAdmin . Um inline permite que você inclua um modelo na mesma edição
página como seu modelo relacionado.
[ 257 ]
Página 283
https://translate.googleusercontent.com/translate_f 250/504
16/12/2021 10:51 Sem título
[ 258 ]
Página 284
Capítulo 7
Primeiro, você precisa de um formulário para inserir os detalhes do pedido. Crie um novo arquivo dentro dos pedidos
diretório do aplicativo e nomeie-o forms.py . Adicione o seguinte código a ele:
Este é o formulário que você usará para criar novos objetos Order . Agora você
precisa de uma visão para lidar com o formulário e criar um novo pedido. Edite o arquivo views.py do
aplicativo de pedidos e adicione o seguinte código a ele:
https://translate.googleusercontent.com/translate_f 251/504
16/12/2021 10:51 Sem título
retornar render (pedido,
'orders / order / create.html',
{'carrinho': carrinho, 'formulário': formulário})
[ 259 ]
Página 285
app_name = 'pedidos'
urlpatterns = [
caminho ('criar /', views.order_create, name = 'order_create'),
]
Este é o padrão de URL para a visualização order_create . Edite o arquivo urls.py do myshop
e inclua o seguinte padrão. Lembre-se de colocá-lo antes do padrão shop.urls :
Os usuários agora podem navegar da página de detalhes do carrinho até o formulário de pedido.
Você ainda precisa definir modelos para fazer pedidos. Crie o seguinte arquivo
estrutura dentro do diretório do aplicativo de pedidos :
modelos/
pedidos /
pedido/
create.html
created.html
[ 260 ]
https://translate.googleusercontent.com/translate_f 252/504
16/12/2021 10:51 Sem título
Página 286
Capítulo 7
{% block title%}
Confira
{% endblock%}
{% block content%}
<h1> Check-out </h1>
Este modelo exibe os itens do carrinho, incluindo totais e o formulário para fazer um pedido.
{% block title%}
Obrigada
{% endblock%}
{% block content%}
<h1> Obrigado </h1>
<p> Seu pedido foi concluído com sucesso. O número do seu pedido é
<strong> {{order.id}} </strong>. </p>
{% endblock%}
[ 261 ]
https://translate.googleusercontent.com/translate_f 253/504
16/12/2021 10:51 Sem título
Página 287
Este é o modelo que você renderiza quando o pedido é criado com sucesso.
Inicie o servidor de desenvolvimento da web para carregar novos arquivos. Abra http://127.0.0.1:8000/
em seu navegador, adicione alguns produtos ao carrinho e prossiga para a finalização da compra
página. Você verá uma página como esta:
Figura 7.10: A página de criação do pedido, incluindo o formulário de checkout do gráfico e detalhes do pedido
Preencha o formulário com os dados válidos e clique no botão Fazer pedido . O pedido vai
ser criado e você verá uma página de sucesso como esta:
[ 262 ]
Página 288
https://translate.googleusercontent.com/translate_f 254/504
16/12/2021 10:51 Sem título
Capítulo 7
Celery é uma fila de tarefas distribuída que pode processar grandes quantidades de mensagens. Usando
Aipo, não só você pode criar tarefas assíncronas facilmente e permitir que sejam executadas
pelos trabalhadores o mais rápido possível, mas você também pode programá-los para serem executados em uma
Tempo.
Instalando o aipo
Vamos instalar o Celery e integrá-lo ao seu projeto. Instale o aipo via pip usando o
seguinte comando:
[ 263 ]
Página 289
Instalando RabbitMQ
Existem várias opções para um agente de mensagens para Celery, incluindo armazenamentos de chave / valor
https://translate.googleusercontent.com/translate_f 255/504
16/12/2021 10:51 Sem título
como o Redis ou um sistema de mensagens real como o RabbitMQ. Vamos configurar o aipo
com RabbitMQ, uma vez que é o trabalhador de mensagem recomendado para Celery. RabbitMQ
é leve, suporta vários protocolos de mensagens e pode ser usado quando
escalabilidade e alta disponibilidade são necessárias.
Se você estiver usando Linux, você pode instalar RabbitMQ a partir do shell usando o seguinte
comando:
Se você precisa instalar o RabbitMQ no macOS ou Windows, você pode encontrar o autônomo
versões em https://www.rabbitmq.com/download.html . Neste site, você também pode
encontrar guias de instalação detalhados para diferentes distribuições de Linux, outros operacionais
sistemas e contêineres.
rabbitmq-server
importar os
de importação de aipo Aipo
[ 264 ]
Página 290
Capítulo 7
Você precisa importar o módulo de aipo no arquivo __init__.py do seu projeto para
certifique-se de que esteja carregado quando o Django iniciar. Edite o arquivo myshop / __ init__.py e
adicione o seguinte código a ele:
# import aipo
do aplicativo de importação .celery como celery_app
Agora você pode começar a programar tarefas assíncronas para seus aplicativos.
Crie um novo arquivo dentro do aplicativo de pedidos e nomeie-o como tasks.py . Isto é o
lugar onde Celery irá procurar tarefas assíncronas. Adicione o seguinte código a ele:
@tarefa
def order_created (order_id):
"" "
[ 265 ]
Página 291
Você define a tarefa order_created usando o decorador de tarefas . Como você pode ver,
uma tarefa Celery é apenas uma função Python decorada com @task . Sua função de tarefa
recebe um parâmetro order_id . É sempre recomendável passar apenas IDs para a tarefa
https://translate.googleusercontent.com/translate_f 257/504
16/12/2021 10:51 Sem título
funções e objetos de pesquisa quando a tarefa é executada. Você usa send_mail ()
função fornecida pelo Django para enviar uma notificação por e-mail ao usuário que colocou
a ordem.
Você aprendeu como configurar o Django para usar seu servidor SMTP no Capítulo 2 ,
Aprimorando seu blog com recursos avançados . Se você não quiser definir as configurações de e-mail,
você pode dizer ao Django para escrever e-mails no console, adicionando a seguinte configuração a
o arquivo settings.py :
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Agora você tem que adicionar a tarefa à sua visão order_create . Edite o arquivo views.py
do aplicativo de pedidos , importe a tarefa e chame o método assíncrono order_created
tarefa após limpar o carrinho, da seguinte maneira:
[ 266 ]
Página 292
Capítulo 7
if request.method == 'POST':
# ...
if form.is_valid ():
# ...
cart.clear ()
# iniciar tarefa assíncrona
order_created.delay (order.id)
# ...
Você chama o método delay () da tarefa para executá-la de forma assíncrona. A tarefa vai
será adicionado à fila e será executado por um trabalhador o mais rápido possível.
Abra outro shell e inicie o trabalhador Celery a partir do diretório do seu projeto, usando
o seguinte comando:
O trabalhador Celery agora está em execução e pronto para processar tarefas. Certifique-se de que o
O servidor de desenvolvimento Django também está rodando.
https://translate.googleusercontent.com/translate_f 258/504
16/12/2021 10:51 Sem título
...
A tarefa foi executada e uma notificação por e-mail para o seu pedido foi enviada ou
exibido na saída do trabalhador do Celery se você estiver usando o back-end de e-mail do console.
Monitoramento de aipo
Você pode querer monitorar as tarefas assíncronas que são executadas. Flor é uma
ferramenta baseada na web para monitorar o aipo. Você pode instalar o Flower usando este comando:
[ 267 ]
Página 293
Abra http: // localhost: 5555 / dashboard em seu navegador. Você será capaz de ver
os trabalhadores ativos do Celery e as estatísticas de tarefas assíncronas:
Resumo
Neste capítulo, você criou um aplicativo básico de loja. Você fez um catálogo de produtos
e construiu um carrinho de compras usando sessões. Você implementou um contexto personalizado
processador para disponibilizar o carrinho para seus modelos e criar um formulário para
fazer pedidos. Você também aprendeu como iniciar tarefas assíncronas com Celery.
https://translate.googleusercontent.com/translate_f 259/504
16/12/2021 10:51 Sem título
[ 268 ]
Página 294
Gerenciando Pagamentos
8
e pedidos
No capítulo anterior, você criou uma loja online básica com um catálogo de produtos
e um carrinho de compras. Você também aprendeu como iniciar tarefas assíncronas com Celery.
Neste capítulo, você aprenderá como integrar um gateway de pagamento ao seu site
para permitir que os usuários paguem com cartão de crédito. Você também irá estender o site de administração com
recursos diferentes.
https://translate.googleusercontent.com/translate_f 260/504
16/12/2021 10:51 Sem título
a um terceiro confiável e seguro. Você não terá que se preocupar com o processamento de crédito
cartões em seu próprio sistema.
Existem vários provedores de gateway de pagamento para você escolher. Você está indo
para integrar o Braintree, que é usado por serviços online populares como o Uber
e Airbnb.
[ 269 ]
Página 295
Braintree fornece uma API que permite processar pagamentos online com vários
métodos de pagamento, como cartão de crédito, PayPal, Google Pay e Apple Pay. Você pode
saiba mais sobre Braintree em https://www.braintreepayments.com/.
Certos campos de pagamento na página de checkout, como o número do cartão de crédito, CVV
número, ou data de expiração, deve ser hospedado de forma segura. A integração dos campos hospedados
hospeda os campos de checkout no domínio do gateway de pagamento e renderiza um iframe
para apresentar os campos aos usuários. Isso fornece a capacidade de personalizar a aparência
e sentir o formulário de pagamento, garantindo que você está em conformidade com o pagamento
Requisitos da indústria de cartões ( PCI ). Já que você pode personalizar a aparência do
campos de formulário, os usuários não perceberão o iframe.
[ 270 ]
https://translate.googleusercontent.com/translate_f 261/504
16/12/2021 10:51 Sem título
Página 296
Capítulo 8
Preencha os detalhes para criar uma nova conta sandbox. Você receberá um email
de Braintree com um link. Siga o link e conclua a configuração da sua conta.
Quando terminar, faça login em https://sandbox.braintreegateway.com/login.
Seu ID de comerciante e chaves públicas / privadas serão exibidos assim:
Você precisará dessas informações para autenticar solicitações para a API Braintree. Sempre
mantenha sua chave privada em segredo.
# Configurações Braintree
BRAINTREE_MERCHANT_ID = 'XXX' # ID do comerciante
BRAINTREE_PUBLIC_KEY = 'XXX' # Chave Pública
BRAINTREE_PRIVATE_KEY = 'XXX' # chave privada
BRAINTREE_CONF = braintree.Configuration (
braintree.Environment.Sandbox,
BRAINTREE_MERCHANT_ID,
BRAINTREE_PUBLIC_KEY,
BRAINTREE_PRIVATE_KEY
)
[ 271 ]
https://translate.googleusercontent.com/translate_f 262/504
16/12/2021 10:51 Sem título
Página 297
Você vai criar um novo aplicativo para gerenciar pagamentos. Crie um novo
aplicativo em seu projeto usando o seguinte comando:
INSTALLED_APPS = [
# ...
'payment.apps.PaymentConfig' ,
]
Depois que os clientes fazem um pedido, você precisa redirecioná-los para o processo de pagamento. Editar
o arquivo views.py do aplicativo de pedidos e inclui as seguintes importações:
[ 272 ]
Página 298
https://translate.googleusercontent.com/translate_f 263/504
16/12/2021 10:51 Sem título
Capítulo 8
Com este código, depois de criar um pedido com sucesso, você define o ID do pedido no
sessão atual usando a chave de sessão order_id . Então, você redireciona o usuário para o
pagamento: URL do processo , que você implementará posteriormente. Lembre-se que você
precisa executar o Celery para que a tarefa order_created seja enfileirada e executada.
Vamos sincronizar este campo com o banco de dados. Use o seguinte comando para gerar
migrações:
[ 273 ]
Página 299
As alterações do modelo agora estão sincronizadas com o banco de dados. Agora você pode armazenar o
ID de transação Braintree para cada pedido. Vamos integrar o gateway de pagamento.
https://translate.googleusercontent.com/translate_f 264/504
16/12/2021 10:51 Sem título
Vamos criar uma visão para processamento de pagamentos. Todo o processo de checkout funcionará
do seguinte modo:
Vamos começar com a visão de checkout de pagamento. Edite o arquivo views.py do pagamento
aplicativo e adicione o seguinte código a ele:
[ 274 ]
Página 300
Capítulo 8
if request.method == 'POST':
# recuperar nonce
nonce = request.POST.get ('payment_method_nonce', None)
# criar e enviar transação
resultado = gateway.transaction.sale ({
'quantia': f '{total_cost: .2f}',
'payment_method_nonce': nonce,
https://translate.googleusercontent.com/translate_f 265/504
16/12/2021 10:51 Sem título
'opções': {
'submit_for_settlement': Verdadeiro
}
})
if result.is_success:
# marca o pedido como pago
order.paid = True
# armazena o id único da transação
order.braintree_id = result.transaction.id
order.save ()
retorno de redirecionamento ('pagamento: concluído')
senão:
retorno de redirecionamento ('pagamento: cancelado')
senão:
# gerar token
client_token = gateway.client_token.generate ()
retornar render (pedido,
'payment / process.html',
{'pedido': pedido,
'client_token': client_token})
1. Você obtém o pedido atual da chave de sessão order_id , que foi armazenada
anteriormente na sessão pela visão order_create .
2. Você recupera o objeto Order para o ID fornecido ou gera uma exceção Http404
se não for encontrado.
[ 275 ]
Página 301
3. Quando a visualização é carregada com uma solicitação POST , você recupera o payment_
method_nonce para gerar uma nova transação usando gateway.transaction.
venda () . Você passa os seguintes parâmetros para ele:
° montante : o valor total a ser cobrado do cliente. Isto é uma corda
com o valor total formatado com duas casas decimais.
https://translate.googleusercontent.com/translate_f 266/504
16/12/2021 10:51 Sem título
5. Se a visualização foi carregada com uma solicitação GET , gere um token de cliente com
gateway.client_token.generate () que você usará no modelo
para instanciar o cliente Braintree JavaScript.
Vamos criar visualizações básicas para redirecionar os usuários quando o pagamento for bem-sucedido,
ou quando foi cancelado por qualquer motivo. Adicione o seguinte código ao views.py
arquivo do aplicativo de pagamento :
app_name = 'pagamento'
urlpatterns = [
caminho ('processo /', visualizações.pagamento_processo, nome = 'processo'),
caminho ('feito /', visualizações.pagamento_done, nome = 'concluído'),
caminho ('cancelado /', visualizações.pagamento_cancelado, nome = 'cancelado'),
]
[ 276 ]
Página 302
Capítulo 8
Estes são os URLs para o fluxo de trabalho de pagamento. Você incluiu o seguinte
Padrões de URL:
Edite o arquivo urls.py principal do projeto myshop e inclua os padrões de URL para
o aplicativo de pagamento , da seguinte forma:
urlpatterns = [
# ...
caminho ('pagamento /', incluir ('pagamento.urls', espaço de nomes = 'pagamento')),
path ('', include ('shop.urls', namespace = 'shop')),
]
modelos/
Forma de pagamento/
https://translate.googleusercontent.com/translate_f 267/504
16/12/2021 10:51 Sem título
process.html
done.html
cancelado.html
{% block content%}
<h1> Pague com cartão de crédito </h1>
<form id = "payment" method = "post">
[ 277 ]
Página 303
braintree.client.create ({
autorização: '{{client_token}}'
}, function (clientErr, clientInstance) {
if (clientErr) {
console.error (clientErr);
Retorna;
}
braintree.hostedFields.create ({
cliente: clientInstance,
estilos: {
'input': {'font-size': '13px'},
'input.invalid': {'color': 'red'},
'input.valid': {'color': 'green'}
},
Campos: {
https://translate.googleusercontent.com/translate_f 268/504
16/12/2021 10:51 Sem título
número: {selector: '# card-number'},
cvv: {selector: '#cvv'},
expirationDate: {selector: '# expiration-date'}
}
}, function (installedFieldsErr, HostFieldsInstance) {
if (installedFieldsErr) {
console.error (installedFieldsErr);
Retorna;
}
submit.removeAttribute ('disabled');
[ 278 ]
Página 304
Capítulo 8
https://translate.googleusercontent.com/translate_f 269/504
16/12/2021 10:51 Sem título
4. Você especifica os seletores de id para os campos card-number , cvv e
data de expiração .
[ 279 ]
Página 305
{% block content%}
<h1> Seu pagamento foi bem-sucedido </h1>
<p> Seu pagamento foi processado com sucesso. </p>
{% endblock%}
{% block content%}
<h1> Seu pagamento não foi processado </h1>
<p> Ocorreu um problema ao processar o seu pagamento. </p>
{% endblock%}
Este é o modelo para a página para a qual o usuário é redirecionado quando a transação
não teve sucesso. Vamos tentar o processo de pagamento.
Testando pagamentos
Abra um shell e execute RabbitMQ com o seguinte comando:
rabbitmq-server
[ 280 ]
https://translate.googleusercontent.com/translate_f 270/504
16/12/2021 10:51 Sem título
Página 306
Capítulo 8
Abra outro shell e inicie o Celery worker a partir do diretório do seu projeto com o
seguinte comando:
Você pode dar uma olhada no código-fonte HTML para ver o HTML gerado.
[ 281 ]
https://translate.googleusercontent.com/translate_f 271/504
16/12/2021 10:51 Sem título
Página 307
Braintree fornece uma lista de cartões de crédito bem e malsucedidos para que você possa
teste todos os cenários possíveis. Você pode encontrar a lista de cartões de crédito para teste em https: //
developers.braintreepayments.com/guides/credit-cards/testing-go-
live / python . Você vai usar o cartão de teste VISA 4111 1111 1111 1111 ,
que retorna uma compra bem-sucedida. Você vai usar CVV 123 e qualquer
data de expiração futura, como 28/12 . Insira os detalhes do cartão de crédito da seguinte forma:
Figura 8.4: O formulário de pagamento com os detalhes do cartão de crédito de teste válido
A transação foi processada com sucesso. Agora você pode entrar em sua conta
em https://sandbox.braintreegateway.com/login . Em Transações , você irá
ser capaz de ver a transação:
[ 282 ]
Página 308
https://translate.googleusercontent.com/translate_f 272/504
16/12/2021 10:51 Sem título
Capítulo 8
Observe que a visão payment_process não lida com recusas de transações. Braintree
fornece a você os códigos de resposta do processador que são retornados pelo cartão de crédito
processador. Eles são especialmente úteis para saber por que uma transação pode ter sido
recusou. Você pode obter um código de resposta usando result.transaction.processor_
response_code e seu texto de resposta associado usando result.transaction.
processador_response_text . Você pode encontrar a lista de autorização de pagamento
respostas em https://developers.braintreepayments.com/reference/general/
Respostas do processador / Respostas da autorização.
Indo ao vivo
Depois de testar seu ambiente, você pode criar uma conta Braintree real em
https://www.braintreepayments.com . Quando estiver pronto para entrar em produção,
lembre-se de alterar suas credenciais de ambiente ao vivo em settings.py
arquivo do seu projeto e use braintree.Environment.Production para configurar
seu ambiente. Todas as etapas para a ativação estão resumidas emhttps: // developers.
braintreepayments.com/start/go-live/python . Além disso, você pode
leia o Capítulo 14 , Going Live , para aprender como definir as configurações do projeto para vários
ambientes.
[ 283 ]
Página 309
Você pode criar uma ação personalizada escrevendo uma função regular que recebe o
seguintes parâmetros:
[ 284 ]
Página 310
Capítulo 8
Você irá criar uma ação de administração personalizada para baixar uma lista de pedidos
como um arquivo CSV. Edite o arquivo admin.py do aplicativo de pedidos e adicione o seguinte
código antes da classe OrderAdmin :
importar csv
importar data e hora
de django.http import HttpResponse
https://translate.googleusercontent.com/translate_f 274/504
16/12/2021 10:51 Sem título
[ 285 ]
Página 311
5. Você itera sobre o QuerySet fornecido e escreve uma linha para cada objeto retornado
pelo QuerySet. Você cuida da formatação de objetos datetime porque o
o valor de saída para CSV deve ser uma string.
6. Você personaliza o nome de exibição da ação no menu suspenso de ações
elemento do site de administração, definindo um atributo short_description
na função.
Você criou uma ação de administração genérica que pode ser adicionada a qualquer
Classe ModelAdmin .
https://translate.googleusercontent.com/translate_f 275/504
16/12/2021 10:51 Sem título
Selecione alguns pedidos e escolha a ação Exportar para CSV na caixa de seleção e, em seguida,
clique no botão Go . Seu navegador fará o download do arquivo CSV gerado denominado
order.csv. Abra o arquivo baixado usando um editor de texto. Você deve ver o conteúdo
com o seguinte formato, incluindo uma linha de cabeçalho e uma linha para cada objeto Order
você selecionou:
[ 286 ]
Página 312
Capítulo 8
Como você pode ver, a criação de ações de administração é bastante simples. Você
pode aprender mais sobre como gerar arquivos CSV com Django em https: // docs.
djangoproject.com/en/3.0/howto/outputting-csv/ .
Vamos criar uma visualização personalizada para exibir informações sobre um pedido. Edite o views.py
https://translate.googleusercontent.com/translate_f 276/504
16/12/2021 10:51 Sem título
arquivo do aplicativo de pedidos e adicione o seguinte código a ele:
@staff_member_required
def admin_order_detail (pedido, pedido_id):
pedido = get_object_or_404 (pedido, id = pedido_id)
retornar render (pedido,
'admin / orders / order / detail.html',
{'pedido': pedido})
[ 287 ]
Página 313
Crie a seguinte estrutura de arquivo dentro dos modelos / diretório dos pedidos
aplicativo:
admin /
pedidos /
pedido/
detail.html
{% block title%}
Pedido {{order.id}} {{block.super}}
{% endblock%}
{% block breadcrumbs%}
<div class = "breadcrumbs">
<a href="{% url "admin:index" %}"> Página inicial </a> & rsaquo;
<a href="{% url "admin:orders_order_changelist" %}"> Pedidos </a>
& rsaquo;
<a href="{% url "admin:orders_order_change" order.id %}"> Pedido {{
order.id}} </a>
& rsaquo; Detalhe
</div>
{% endblock%}
https://translate.googleusercontent.com/translate_f 277/504
16/12/2021 10:51 Sem título
{% block content%}
<h1> Pedido {{order.id}} </h1>
<ul class = "object-tools">
<li>
<a href="#" onclick="window.print();"> Pedido de impressão </a>
</li>
</ul>
<table>
<tr>
<th> Criado </th>
<td> {{order.created}} </td>
</tr>
<tr>
[ 288 ]
Página 314
Capítulo 8
https://translate.googleusercontent.com/translate_f 278/504
16/12/2021 10:51 Sem título
[ 289 ]
Página 315
{% endfor%}
<tr class = "total">
<td colspan = "3"> Total </td>
<td class = "num"> $ {{order.get_total_cost}} </td>
</tr>
</tbody>
</table>
</div>
{% endblock%}
Quando você deseja estender um modelo de administração, você precisa conhecer sua estrutura
e identificar os blocos existentes. Você pode encontrar todos os modelos de administração em https: //
github.com/django/django/tree/3.0/django/contrib/admin/templates/admin.
Você também pode substituir um modelo de administração, se necessário. Para fazer isso, copie
um modelo em seus modelos / diretório, mantendo o mesmo caminho relativo e
nome do arquivo. O site de administração do Django usará seu modelo personalizado em vez de
o padrão.
Finalmente, vamos adicionar um link para cada objeto Order na página de exibição da lista do
site de administração. Edite o arquivo admin.py do aplicativo de pedidos e adicione
o seguinte código para ele, acima da classe OrderAdmin :
Esta é uma função que recebe um objeto Order como um argumento e retorna um HTML
link para o URL admin_order_detail . Django escapa da saída HTML por padrão.
Você deve usar a função mark_safe para evitar o escape automático.
[ 290 ]
https://translate.googleusercontent.com/translate_f 279/504
16/12/2021 10:51 Sem título
Página 316
Capítulo 8
Clique no link Exibir de qualquer pedido para carregar a página de detalhes do pedido personalizado. Você
deve ver uma página como a seguinte:
[ 291 ]
https://translate.googleusercontent.com/translate_f 280/504
16/12/2021 10:51 Sem título
Página 317
Na maioria dos casos, você terá que adicionar estilos e formatação personalizados ao seu PDF
arquivos. Você achará mais conveniente renderizar um template HTML e convertê-lo
em um arquivo PDF, mantendo o Python longe da camada de apresentação. Você está indo
seguir esta abordagem e usar um módulo para gerar arquivos PDF com Django. Você
usará WeasyPrint, que é uma biblioteca Python que pode gerar arquivos PDF a partir de
Modelos HTML.
Instalando WeasyPrint
Primeiro, instale as dependências do WeasyPrint para o seu sistema operacional de https: //
weasyprint.readthedocs.io/en/latest/install.html . Em seguida, instale o WeasyPrint
via pip usando o seguinte comando:
<html>
<body>
<h1> Minha loja </h1>
p
Fatura nº {{order.id}} </br>
<span class = "secondary">
{{order.created | date: "M d, Y"}}
</span>
</p>
[ 292 ]
Página 318
https://translate.googleusercontent.com/translate_f 281/504
16/12/2021 10:51 Sem título
Capítulo 8
Este é o modelo para a fatura em PDF. Neste modelo, você exibe todos os pedidos
detalhes e um elemento HTML <table> incluindo os produtos. Você também inclui
uma mensagem para mostrar se o pedido foi pago.
[ 293 ]
Página 319
@staff_member_required
def admin_order_pdf (pedido, pedido_id):
pedido = get_object_or_404 (pedido, id = pedido_id)
html = render_to_string ('orders / order / pdf.html',
{'pedido': pedido})
resposta = HttpResponse (content_type = 'aplicativo / pdf')
resposta ['Content-Disposition'] = f'filename = order_ {order.id} .pdf '
weasyprint.HTML (string = html) .write_pdf (resposta,
folhas de estilo = [weasyprint.CSS (
settings.STATIC_ROOT + 'css / pdf.css')])
resposta de retorno
Esta é a visão para gerar uma fatura em PDF para um pedido. Você usa o staff_member_
decorador necessário para garantir que apenas os usuários da equipe possam acessar esta visualização.
Você obtém o objeto Order com o ID fornecido e usa render_to_string ()
função fornecida pelo Django para renderizar orders / order / pdf.html . O renderizado
HTML é salvo na variável html .
Use o arquivo estático css / pdf.css para adicionar estilos CSS ao arquivo PDF gerado. Então,
você o carrega do caminho local usando a configuração STATIC_ROOT . Finalmente, você retorna
a resposta gerada.
Se você não tiver os estilos CSS, lembre-se de copiar os arquivos estáticos localizados no
estático / diretório do aplicativo da loja para o mesmo local do seu projeto.
Você pode encontrar o conteúdo do diretório em https://github.com/PacktPublishing/
Django-3-by-Example / tree / master / Chapter08 / myshop / shop / static .
Visto que você precisa usar a configuração STATIC_ROOT , você deve adicioná-la ao seu projeto.
Este é o caminho do projeto onde residem os arquivos estáticos. Edite o arquivo settings.py do
projeto myshop e adicione a seguinte configuração:
[ 294 ]
Página 320
Capítulo 8
https://translate.googleusercontent.com/translate_f 283/504
16/12/2021 10:51 Sem título
fontes de arquivos estáticos adicionais na configuração STATICFILES_DIRS . Todos os diretórios
especificado na lista STATICFILES_DIRS também será copiado para o STATIC_ROOT
diretório quando o collectstatic é executado. Sempre que você executa o collectstatic
novamente, você será questionado se deseja substituir os arquivos estáticos existentes.
urlpatterns = [
# ...
caminho ('admin / order / <int: order_id> / pdf /',
views.admin_order_pdf,
name = 'admin_order_pdf'),
]
Agora você pode editar a página de exibição da lista de administração para o modelo de pedido para
adicione um link para o arquivo PDF para cada resultado. Edite o arquivo admin.py dentro dos pedidos
aplicativo e adicione o seguinte código acima da classe OrderAdmin :
Se você especificar um atributo short_description para seu callable, Django irá usá-lo para
O nome da coluna.
[ 295 ]
Página 321
Clique no link do PDF para qualquer pedido. Você deve ver um arquivo PDF gerado como o
seguinte para pedidos que ainda não foram pagos:
https://translate.googleusercontent.com/translate_f 284/504
16/12/2021 10:51 Sem título
[ 296 ]
Página 322
Capítulo 8
https://translate.googleusercontent.com/translate_f 285/504
16/12/2021 10:51 Sem título
Crie um novo arquivo dentro do diretório do aplicativo de pagamento e nomeie-o como tasks.py .
Adicione o seguinte código a ele:
do io importar BytesIO
da tarefa de importação de aipo
importar impressão weasy
[ 297 ]
Página 323
@tarefa
def payment_completed (order_id):
"" "
Tarefa para enviar uma notificação por e-mail quando um pedido é
criado com sucesso.
"" "
pedido = Order.objects.get (id = order_id)
https://translate.googleusercontent.com/translate_f 286/504
16/12/2021 10:51 Sem título
# enviar email
email.send ()
[ 298 ]
Página 324
Capítulo 8
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
if request.method == 'POST':
# recuperar nonce
nonce = request.POST.get ('payment_method_nonce', None)
# criar e enviar transação
resultado = gateway.transaction.sale ({
'quantia': f '{total_cost: .2f}',
'payment_method_nonce': nonce,
'opções': {
'submit_for_settlement': Verdadeiro
}
})
if result.is_success:
# marca o pedido como pago
order.paid = True
https://translate.googleusercontent.com/translate_f 287/504
16/12/2021 10:51 Sem título
# armazena o id único da transação
order.braintree_id = result.transaction.id
order.save ()
# iniciar tarefa assíncrona
payment_completed.delay (order.id)
retorno de redirecionamento ('pagamento: concluído')
[ 299 ]
Página 325
senão:
retorno de redirecionamento ('pagamento: cancelado')
senão:
# gerar token
client_token = gateway.client_token.generate ()
retornar render (pedido,
'payment / process.html',
{'pedido': pedido,
'client_token': client_token})
Agora você pode concluir um novo processo de pagamento para receber a fatura em PDF
em seu e-mail.
Resumo
Neste capítulo, você integrou o gateway de pagamento Braintree ao seu projeto
usando a integração de campos hospedados. Você construiu uma ação de administração personalizada para
pedidos de exportação para CSV. Você também personalizou o site de administração do Django usando
visualizações e modelos personalizados. Finalmente, você aprendeu como gerar arquivos PDF com
WeasyPrint e como enviá-los por e-mail.
[ 300 ]
https://translate.googleusercontent.com/translate_f 288/504
16/12/2021 10:51 Sem título
Página 326
Neste capítulo, você adicionará um sistema de cupons à sua loja. Você também aprenderá como
trabalho de internacionalização e localização, e você criará uma recomendação
motor.
Você vai criar um sistema de cupons para sua loja. Seus cupons serão válidos
para clientes em um determinado período. Os cupons não terão limitações
em termos do número de vezes que eles podem ser resgatados, e eles serão aplicados a
o valor total do carrinho de compras. Para esta funcionalidade, você precisará criar
um modelo para armazenar o código do cupom, um período de tempo válido e o desconto a ser aplicado.
[ 301 ]
https://translate.googleusercontent.com/translate_f 289/504
16/12/2021 10:51 Sem título
Página 327
INSTALLED_APPS = [
# ...
'coupons.apps.CouponsConfig',
]
Este é o modelo que você utilizará para armazenar cupons. O modelo de cupom
contém os seguintes campos:
• código : o código que os usuários devem inserir para aplicar o cupom a seus
comprar.
• valid_from : o valor de data e hora que indica quando o cupom se torna
válido.
[ 302 ]
Página 328
https://translate.googleusercontent.com/translate_f 290/504
16/12/2021 10:51 Sem título
Capítulo 9
As migrações agora são aplicadas no banco de dados. Vamos adicionar o modelo de cupom ao
site de administração. Edite o arquivo admin.py do aplicativo de cupons e adicione o
seguinte código para ele:
@ admin.register (cupom)
classe CouponAdmin (admin.ModelAdmin):
list_display = ['code', 'valid_from', 'valid_to',
'desconto', 'ativo']
list_filter = ['ativo', 'válido_de', 'válido_para']
search_fields = ['código']
O modelo de cupom agora está registrado no site de administração. Certifique-se de que seu
o servidor local está sendo executado com o comando python manage.py runserver . Aberto
http://127.0.0.1:8000/admin/coupons/coupon/add/ em seu navegador.
[ 303 ]
Página 329
https://translate.googleusercontent.com/translate_f 291/504
16/12/2021 10:51 Sem título
Preencha o formulário para criar um novo cupom válido para a data atual e faça
certifique-se de marcar a caixa de seleção Ativo e clique no botão SALVAR .
[ 304 ]
Página 330
Capítulo 9
https://translate.googleusercontent.com/translate_f 292/504
16/12/2021 10:51 Sem título
Crie um novo arquivo dentro do diretório do aplicativo de cupons e nomeie-o forms.py .
Adicione o seguinte código a ele:
Este é o formulário que você usará para que o usuário insira um código de cupom. Editar
o arquivo views.py dentro do aplicativo de cupons e adicione o seguinte código a ele:
@require_POST
def coupon_apply (solicitação):
now = timezone.now ()
form = CouponApplyForm (request.POST)
if form.is_valid ():
code = form.cleaned_data ['code']
experimentar:
cupom = Coupon.objects.get (code__iexact = code,
valid_from__lte = now,
valid_to__gte = now,
ativo = verdadeiro)
request.session ['coupon_id'] = coupon.id
exceto Coupon.DoesNotExist:
request.session ['coupon_id'] = Nenhum
return redirect ('cart: cart_detail')
[ 305 ]
Página 331
Você precisa de um padrão de URL para a visualização coupon_apply . Crie um novo arquivo dentro do
cupons de diretório de aplicativos e nomeá-la urls.py . Adicione o seguinte código a ele:
app_name = 'cupons'
urlpatterns = [
caminho ('aplicar /', visualizações.coupon_apply, nome = 'aplicar'),
]
Em seguida, edite o urls.py principal do projeto myshop e inclua a URL dos cupons
padrões, como segue:
urlpatterns = [
# ...
caminho ('cupons /', include ('coupons.urls', namespace = 'cupons')),
path ('', include ('shop.urls', namespace = 'shop')),
]
[ 306 ]
Página 332
Capítulo 9
Neste código, você tenta obter a chave de sessão coupon_id da sessão atual e
armazene seu valor no objeto Carrinho . Adicione os seguintes métodos ao objeto Cart :
https://translate.googleusercontent.com/translate_f 294/504
16/12/2021 10:51 Sem título
passar
retornar nenhum
[ 307 ]
Página 333
• coupon () : você define este método como uma propriedade . Se o carrinho contém
um atributo coupon_id , o objeto Coupon com o ID fornecido é retornado.
• get_discount () : se o carrinho contiver um cupom, você recupera o desconto
avalie e devolva o valor a ser deduzido do valor total do carrinho.
• get_total_price_after_discount () : Você retorna o valor total do
cart após deduzir a quantia retornada pelo método get_discount () .
A classe Cart agora está preparada para lidar com um cupom aplicado à sessão atual
e aplicar o desconto correspondente.
Vamos incluir o sistema de cupons na visualização de detalhes do carrinho. Edite o arquivo views.py do
aplicativo de carrinho e adicione a seguinte importação na parte superior do arquivo:
Mais abaixo, edite a visualização cart_detail e adicione o novo formulário a ela, da seguinte maneira:
https://translate.googleusercontent.com/translate_f 295/504
16/12/2021 10:51 Sem título
Edite o modelo
seguintes linhas:cart / detail.html do aplicativo de carrinho e localize o
[ 308 ]
Página 334
Capítulo 9
{% if cart.coupon%}
<tr class = "subtotal">
<td> Subtotal </td>
<td colspan = "4"> </td>
<td class = "num"> $ {{cart.get_total_price | floatformat: 2}} </td>
</tr>
<tr>
<td>
cupom "{{cart.coupon.code}}"
({{cart.coupon.discount}}% de desconto)
</td>
<td colspan = "4"> </td>
<td class = "num neg">
- $ {{cart.get_discount | floatformat: 2}}
</td>
</tr>
{% fim se %}
<tr class = "total">
<td> Total </td>
<td colspan = "4"> </td>
<td class = "num">
$ {{carrinho. get_total_price_after_discount | floatformat: 2 }}
</td>
</tr>
Este é o código para exibir um cupom opcional e sua taxa de desconto. Se o carrinho
contém um cupom, você exibe uma primeira linha, incluindo o valor total do carrinho como
o subtotal. Em seguida, você usa uma segunda linha para exibir o cupom atual aplicado a
o carrinho. Finalmente, você exibe o preço total, incluindo qualquer desconto, chamando o
método get_total_price_after_discount () do objeto carrinho .
</form>
[ 309 ]
Página 335
Isso exibirá o formulário para inserir um código de cupom e aplicá-lo ao carrinho atual.
Figura 9.2: A página de detalhes do carrinho, incluindo detalhes do cupom e um formulário para aplicar um cupom
<ul>
{% para item no carrinho%}
<li>
{{item.quantity}} x {{item.product.name}}
<span> $ {{item.total_price}} </span>
</li>
{% endfor%}
</ul>
<ul>
{% para item no carrinho%}
<li>
{{item.quantity}} x {{item.product.name}}
<span> $ {{item.total_price | floatformat: 2 }} </span>
[ 310 ]
https://translate.googleusercontent.com/translate_f 297/504
16/12/2021 10:51 Sem título
Página 336
Capítulo 9
</li>
{% endfor%}
{% if cart.coupon%}
<li>
"{{cart.coupon.code}}" ({{cart.coupon.discount}}% de desconto)
<span class = "neg"> - $ {{cart.get_discount | floatformat: 2}} </
span>
</li>
{% fim se %}
</ul>
Os usuários agora podem aplicar cupons em seus carrinhos de compras. No entanto, você ainda precisa armazenar
informações do cupom na ordem em que são criadas quando os usuários fazem check-out do carrinho.
[ 311 ]
https://translate.googleusercontent.com/translate_f 298/504
16/12/2021 10:51 Sem título
Página 337
Esses campos permitem que você armazene um cupom opcional para o pedido e o desconto
percentual aplicado com o cupom. O desconto é armazenado no cupom relacionado
objeto, mas você o inclui no modelo de pedido para preservá-lo se o cupom for modificado
ou excluído. Você define on_delete para models.SET_NULL para que se o cupom for
excluído, o campo do cupom é definido como Nulo , mas o desconto é preservado.
Você precisa criar uma migração para incluir os novos campos do modelo de pedido . Execute o
seguinte comando da linha de comando:
[ 312 ]
Página 338
https://translate.googleusercontent.com/translate_f 299/504
16/12/2021 10:51 Sem título
Capítulo 9
Você deve ver uma confirmação indicando que a nova migração foi aplicada.
As alterações do campo Modelo de pedido agora são sincronizadas com o banco de dados.
pedido = form.save ()
[ 313 ]
Página 339
Quando terminar uma compra com sucesso, você pode ir para http://127.0.0.1:8000/
admin / pedidos / pedido / e verifique se o objeto do pedido contém o cupom e
https://translate.googleusercontent.com/translate_f 300/504
16/12/2021 10:51 Sem título
o desconto aplicado, da seguinte forma:
Você também pode modificar o modelo de detalhes do pedido de administração e o PDF do pedido
fatura para exibir o cupom aplicado da mesma forma que você fez para o carrinho.
Adicionando internacionalização e
localização
Django oferece suporte total à internacionalização e localização. Isso permite que você
traduzir seu aplicativo em vários idiomas e lidar com locais específicos
formatação de datas, horas, números e fusos horários. Vamos esclarecer a diferença
entre internacionalização e localização. Internacionalização (frequentemente
abreviado para i18n ) é o processo de adaptação de software para o uso potencial de
diferentes idiomas e localidades, de modo que não seja conectado a um idioma específico
ou local. Localização (abreviado para l10n ) é o processo de tradução
o software e adaptando-o a um local específico. O próprio Django é traduzido para
mais de 50 idiomas usando sua estrutura de internacionalização.
[ 314 ]
Página 340
Capítulo 9
https://translate.googleusercontent.com/translate_f 301/504
16/12/2021 10:51 Sem título
Extensão .mo .
[ 315 ]
Página 341
• makemessages : Este percorre a árvore de origem para encontrar todas as strings marcadas
para tradução e cria ou atualiza os arquivos de mensagem .po no local
diretório. Um único arquivo .po é criado para cada idioma.
• compilemessages : Compila os arquivos de mensagem .po existentes para arquivos .mo
que são usados para recuperar traduções.
Você precisará do kit de ferramentas gettext para poder criar, atualizar e compilar mensagens
arquivos. A maioria das distribuições Linux inclui o kit de ferramentas gettext. Se você estiver usando
macOS, provavelmente a maneira mais simples de instalá-lo é via Homebrew, em https: //
https://translate.googleusercontent.com/translate_f 302/504
16/12/2021 10:51 Sem título
brew.sh/ , com o comando brew install gettext . Você também pode precisar
force a ligação com o comando brew link --force gettext . Para Windows,
siga as etapas em https://docs.djangoproject.com/en/3.0/topics/i18n/
tradução / # gettext-on-windows .
1. Se você estiver usando i18n_patterns , ou seja, você está usando URL traduzido
padrões, ele procura um prefixo de idioma na URL solicitada para determinar
o idioma atual.
[ 316 ]
Página 342
Capítulo 9
IDIOMAS = (
https://translate.googleusercontent.com/translate_f 303/504
16/12/2021 10:51 Sem título
('en', 'Inglês'),
('es', 'espanhol'),
)
LANGUAGE_CODE = 'en'
MIDDLEWARE = [
[ 317 ]
Página 343
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
# ...
]
localidade/
en /
es /
LOCALE_PATHS = (
os.path.join (BASE_DIR, 'locale /'),
)
https://translate.googleusercontent.com/translate_f 304/504
16/12/2021 10:51 Sem título
[ 318 ]
Página 344
Capítulo 9
Traduções padrão
O código a seguir mostra como marcar uma string para tradução:
Traduções preguiçosas
Django inclui versões preguiçosas para todas as suas funções de tradução, que têm o
sufixo _lazy () . Ao usar as funções preguiçosas, as strings são traduzidas quando o valor
é acessado, ao invés de quando a função é chamada (é por isso que eles são traduzidos
preguiçosamente ). As funções de tradução preguiçosa são úteis quando as strings marcadas para
tradução estão em caminhos que são executados quando os módulos são carregados.
Usando espaços reservados, você pode reordenar as variáveis de texto. Por exemplo, um inglês
tradução do exemplo anterior pode ser hoje é 14 de abril , enquanto o espanhol
um pode ser hoy es 14 de Abril . Sempre use interpolação de string em vez de posicional
interpolação quando você tem mais de um parâmetro para a string de tradução.
Ao fazer isso, você poderá reordenar o texto do espaço reservado.
[ 319 ]
Página 345
Agora que você sabe o básico sobre a tradução de literais em seu código Python, é hora
para aplicar traduções ao seu projeto.
IDIOMAS = (
('en', _ ('Inglês')),
('es', _ ('espanhol')),
)
localidades de processamento
processamento local en
Dê uma olhada no diretório locale / . Você deve ver uma estrutura de arquivo como o
Segue:
en /
LC_MESSAGES /
django.po
es /
LC_MESSAGES /
django.po
Um arquivo de mensagem .po foi criado para cada idioma. Abra es / LC_MESSAGES /
django.po com um editor de texto. No final do arquivo, você deve ser capaz de ver o
Segue:
[ 320 ]
https://translate.googleusercontent.com/translate_f 306/504
16/12/2021 10:51 Sem título
Página 346
Capítulo 9
Cada string de tradução é precedido por um comentário mostrando detalhes sobre o arquivo e
a linha onde foi encontrado. Cada tradução inclui duas strings:
django-admin compilemessages
A saída fornece informações sobre os arquivos de mensagens que estão sendo compilados.
Dê uma olhada no diretório local do projeto myshop novamente. Você deveria ver o
seguintes arquivos:
en /
LC_MESSAGES /
django.mo
django.po
es /
LC_MESSAGES /
django.mo
django.po
Você pode ver que um arquivo de mensagem compilado .mo foi gerado para cada idioma.
Você traduziu os próprios nomes dos idiomas. Agora, vamos traduzir o modelo
nomes de campo que são exibidos no site. Edite o arquivo models.py dos pedidos
aplicativo e adicionar nomes marcados para tradução para os campos de modelo de pedido
do seguinte modo:
[ 321 ]
https://translate.googleusercontent.com/translate_f 307/504
16/12/2021 10:51 Sem título
Página 347
Você adicionou nomes para os campos que são exibidos quando um usuário está colocando um novo
pedido. Estes são first_name , last_name , email , address , postal_code e city .
Lembre-se de que você também pode usar o atributo verbose_name para nomear os campos.
localidade/
en /
es /
localidades de processamento
processamento local en
#: orders / models.py: 11
msgid "primeiro nome"
msgstr "nombre"
#: orders / models.py: 12
[ 322 ]
Página 348
https://translate.googleusercontent.com/translate_f 308/504
16/12/2021 10:51 Sem título
Capítulo 9
msgid "sobrenome"
msgstr "apellidos"
#: orders / models.py: 13
msgid "e-mail"
msgstr "e-mail"
#: orders / models.py: 13
msgid "endereço"
msgstr "direção"
#: orders / models.py: 14
msgid "código postal"
msgstr "código postal"
#: orders / models.py: 15
msgid "cidade"
msgstr "ciudad"
Além de um editor de texto, você pode usar o Poedit para editar traduções. Poedit é um software
para editar traduções que usam gettext. Está disponível para Linux, Windows,
e macOS. Você pode baixar o Poedit dehttps://poedit.net/.
[ 323 ]
Página 349
https://translate.googleusercontent.com/translate_f 309/504
16/12/2021 10:51 Sem título
de formulários de importação django
de django.utils.translation import gettext_lazy as _
Traduzindo modelos
Django oferece as tags de template {% trans%} e {% blocktrans%} para traduzir
strings em modelos. Para usar as tags do modelo de tradução, você deve adicionar
{% load i18n%} no topo do seu modelo para carregá-los.
Você pode usar as para armazenar o conteúdo traduzido em uma variável que você pode usar
em todo o seu modelo. O exemplo a seguir armazena o texto traduzido em
uma variável chamada saudação :
A tag {% trans%} é útil para strings de tradução simples, mas não pode lidar
conteúdo para tradução que inclui variáveis.
[ 324 ]
Página 350
Capítulo 9
Você pode usar com para incluir expressões de modelo, como acessar atributos de objeto
ou aplicando filtros de modelo a variáveis. Você sempre tem que usar marcadores de posição para
esses. Você não pode acessar expressões ou atributos de objeto dentro do bloco blocktrans .
O exemplo a seguir mostra como usar com para incluir um atributo de objeto para
qual o filtro capfirst é aplicado:
https://translate.googleusercontent.com/translate_f 310/504
16/12/2021 10:51 Sem título
{% load i18n%}
{% load static%}
<! DOCTYPE html>
<html>
<head>
<meta charset = "utf-8" />
<title>
{% block title%} {% trans "Minha loja"%} {% endblock%}
</title>
<link href = "{% static" css / base.css "%}" rel = "stylesheet">
</head>
<body>
<div id = "header">
<a href="/" class="logo"> {% trans "Minha loja"%} </a>
</div>
<div id = "subheader">
<div class = "cart">
{% com total_items = carrinho | comprimento%}
{% se total_items> 0%}
{% trans "Seu carrinho"%}:
<a href="{% url "cart:cart_detail" %}">
{% blocktrans com contagem total = cart.get_total_price
items = total_items%}
{{items}} item, $ {{total}}
[ 325 ]
Página 351
{% plural %}
{{items}} itens, $ {{total}}
{% endblocktrans%}
</a>
{% senão %}
{% trans "Seu carrinho está vazio." %}
{% fim se %}
{% endwith%}
</div>
</div>
<div id = "content">
{% block content%}
{% endblock%}
</div>
</body>
</html>
https://translate.googleusercontent.com/translate_f 311/504
16/12/2021 10:52 Sem título
Certifique-se de que nenhuma tag de modelo seja dividida em várias linhas.
Observe a tag {% blocktrans%} para exibir o resumo do carrinho. O resumo do carrinho
era anteriormente o seguinte:
Você mudou e agora você usa {% blocktrans com ...%} para configurar o
placeholder total com o valor de cart.get_total_price (o método do objeto
chamado aqui). Você também usa a contagem , que permite definir uma variável para a contagem
objetos para o Django selecionar a forma plural correta. Você define a variável de itens para contar
objetos com o valor de total_items . Isso permite que você defina uma tradução para o
formas no singular e no plural, que você separa com a tag {% plural%} dentro
o bloco {% blocktrans%} . O código resultante é:
{% load i18n%}
[ 326 ]
Página 352
Capítulo 9
<input type = "submit" value = "{% trans" Adicionar ao carrinho "%}" >
{% block title%}
{% trans "Checkout"%}
{% endblock%}
{% block content%}
<h1 > {% trans "Checkout"%} </h1>
[ 327 ]
Página 353
{% csrf_token%}
</form>
{% endblock%}
Certifique-se de que nenhuma tag de modelo seja dividida em várias linhas. Dê uma olhada no
os seguintes arquivos no código que acompanha este capítulo para ver como as strings
foi marcado para tradução:
Vamos atualizar os arquivos de mensagem para incluir as novas strings de tradução. Abra a concha
e execute o seguinte comando:
Os arquivos .po estão dentro do diretório local do projeto myshop e você verá que
o aplicativo de pedidos agora contém todas as strings que você marcou para tradução.
django-admin compilemessages
https://translate.googleusercontent.com/translate_f 313/504
16/12/2021 10:52 Sem título
Um arquivo .mo contendo traduções compiladas foi gerado para cada .po
arquivo de tradução.
[ 328 ]
Página 354
Capítulo 9
INSTALLED_APPS = [
# ...
'rosetta',
]
Você precisa adicionar os URLs do Rosetta à sua configuração de URL principal. Edite o principal
arquivo urls.py do seu projeto e adicione o seguinte padrão de URL a ele:
urlpatterns = [
# ...
caminho ('rosetta /', include ('rosetta.urls')),
path ('', include ('shop.urls', namespace = 'shop')),
]
https://translate.googleusercontent.com/translate_f 314/504
16/12/2021 10:52 Sem título
[ 329 ]
Página 355
Rosetta usa uma cor de fundo diferente para exibir marcadores de posição. Quando você
traduzir o conteúdo, certifique-se de manter os espaços reservados não traduzidos. Por exemplo,
pegue a seguinte string:
[ 330 ]
https://translate.googleusercontent.com/translate_f 315/504
16/12/2021 10:52 Sem título
Página 356
Capítulo 9
Você pode dar uma olhada no código-fonte que vem junto com este capítulo para usar o
mesmas traduções em espanhol para o seu projeto.
Quando você terminar de editar traduções, clique no Salvar e traduzir próximo bloco botão
para salvar as traduções no arquivo .po . Rosetta compila o arquivo de mensagem quando você
salve as traduções, para que não haja necessidade de executar o comando compilemessages .
No entanto, Rosetta requer acesso de gravação aos diretórios de localidade para gravar o
arquivos de mensagens. Certifique-se de que os diretórios tenham permissões válidas.
Traduções difusas
Você deve ter notado que há uma coluna FUZZY no Rosetta. Isso não é
um recurso Rosetta; ele é fornecido por gettext. Se o sinalizador difuso estiver ativo para uma tradução,
ele não será incluído nos arquivos de mensagem compilados. Esta bandeira marca a tradução
strings que precisam ser revisadas por um tradutor. Quando os arquivos .po são atualizados com
novas strings de tradução, é possível que algumas strings de tradução sejam automaticamente
ser sinalizado como difuso. Isso acontece quando gettext encontra algum msgid que foi
ligeiramente modificado. gettext o associa com o que ele pensa ser a tradução antiga e
sinaliza como difuso para revisão. O tradutor deve então revisar as traduções confusas,
remova o sinalizador fuzzy e compile o arquivo de tradução novamente.
[ 331 ]
https://translate.googleusercontent.com/translate_f 316/504
16/12/2021 10:52 Sem título
Página 357
Um motivo para traduzir URLs é otimizar seu site para mecanismos de pesquisa. Por
adicionando um prefixo de idioma aos seus padrões, você será capaz de indexar um URL para cada
idioma em vez de um único URL para todos eles. Além disso, ao traduzir URLs
em cada idioma, você fornecerá aos mecanismos de pesquisa URLs com melhor classificação
para cada idioma.
Vamos adicionar um prefixo de idioma aos seus padrões de URL. Edite o arquivo urls.py principal do
projeto myshop e adicione i18n_patterns () , da seguinte maneira:
urlpatterns = i18n_patterns (
caminho ('admin /', admin.site.urls),
caminho ('cart /', include ('cart.urls', namespace = 'cart')),
caminho ('pedidos /', include ('pedidos.urls', namespace = 'pedidos')),
caminho ('pagamento /', incluir ('pagamento.urls', espaço de nomes = 'pagamento')),
caminho ('cupons /', include ('coupons.urls', namespace = 'cupons')),
caminho ('rosetta /', include ('rosetta.urls')),
path ('', include ('shop.urls', namespace = 'shop')),
)
[ 332 ]
Página 358
https://translate.googleusercontent.com/translate_f 317/504
16/12/2021 10:52 Sem título
Capítulo 9
Edite o arquivo urls.py principal do projeto myshop e adicione strings de tradução para
as expressões regulares dos padrões de URL para o carrinho , pedidos , pagamento e
aplicativos de cupons , como segue:
urlpatterns = i18n_patterns (
caminho ( _ (' admin / ') , admin.site.urls),
caminho ( _ ( 'cart / ') , include ('cart.urls', namespace = 'cart')),
caminho ( _ ( 'pedidos / ') , include ('pedidos.urls', namespace = 'pedidos')),
caminho ( _ (' pagamento / ') , incluir ('pagamento.urls', espaço de nomes = 'pagamento')),
caminho ( _ (' cupons / ') , include ('coupons.urls', namespace = 'cupons')),
caminho ('rosetta /', include ('rosetta.urls')),
path ('', include ('shop.urls', namespace = 'shop')),
)
urlpatterns = [
caminho ( _ (' criar / ') , views.order_create, name = 'order_create'),
# ...
]
[ 333 ]
Página 359
urlpatterns = [
https://translate.googleusercontent.com/translate_f 318/504
16/12/2021 10:52 Sem título
caminho ( _ (' processo / ') , visualizações.pagamento_processo, nome = 'processo'),
caminho ( _ (' feito / ') , visualizações.pagamento_done, nome = 'concluído'),
caminho ( _ (' cancelado / ') , visualizações.pagamento_cancelado, nome = 'cancelado'),
]
Você não precisa traduzir os padrões de URL do aplicativo da loja , uma vez que eles são
construído com variáveis e não inclui quaisquer outros literais.
Abra o shell e execute o próximo comando para atualizar os arquivos de mensagem com o novo
traduções:
<div id = "header">
<a href="/" class="logo"> {% trans "Minha loja"%} </a>
</div>
<div id = "header">
<a href="/" class="logo"> {% trans "Minha loja"%} </a>
{% get_current_language as LANGUAGE_CODE%}
{% get_available_languages as LANGUAGES%}
{% get_language_info_list para LANGUAGES como idiomas%}
<div class = "languages">
<p> {% trans "Língua"%}: </p>
<ul class = "languages">
{% para idioma em idiomas%}
<li>
[ 334 ]
Página 360
Capítulo 9
https://translate.googleusercontent.com/translate_f 319/504
16/12/2021 10:52 Sem título
Certifique-se de que nenhuma tag de modelo seja dividida em várias linhas.
Figura 9.8: A página da lista de produtos, incluindo um seletor de idioma no cabeçalho do site
[ 335 ]
Página 361
Os usuários agora podem alternar facilmente para o idioma de sua preferência clicando nele.
django-parler gera uma tabela de banco de dados separada para cada modelo que contém
traduções. Esta tabela inclui todos os campos traduzidos e uma chave estrangeira para o
objeto original ao qual a tradução pertence. Ele também contém um campo de idioma,
já que cada linha armazena o conteúdo para um único idioma.
https://translate.googleusercontent.com/translate_f 320/504
16/12/2021 10:52 Sem título
Instalando django-parler
Instale o django-parler via pip usando o seguinte comando:
INSTALLED_APPS = [
# ...
'parler',
]
PARLER_LANGUAGES = {
Nenhum: (
{'code': 'en'},
{'code': 'es'},
),
'padrão': {
'fallback': 'en',
'hide_untranslated': False,
}
}
[ 336 ]
Página 362
Capítulo 9
Em seguida, modifique o modelo de categoria para tornar os campos de nome e slug traduzíveis,
do seguinte modo:
https://translate.googleusercontent.com/translate_f 321/504
16/12/2021 10:52 Sem título
O modelo de categoria agora herda de TranslatableModel em vez de modelos.
O modelo e os campos de nome e slug estão incluídos no TranslatedFields
embrulho.
Edite o modelo do produto para adicionar traduções para o nome , slug e descrição
campos, como segue:
[ 337 ]
Página 363
Figura 9.9: O modelo de produto e o modelo de tradução de produto relacionado gerado por django-parler
Já que o Django usa uma tabela separada para traduções, existem alguns recursos do Django
que você não pode usar. Não é possível usar uma ordenação padrão por um campo traduzido.
Você pode filtrar por campos traduzidos em consultas, mas não pode incluir um traduzível
nas opções de Meta de pedido .
https://translate.googleusercontent.com/translate_f 322/504
16/12/2021 10:52 Sem título
class Category (TranslatableModel):
# ...
classe Meta:
# pedido = ('nome',)
verbose_name = 'categoria'
verbose_name_plural = 'categorias'
[ 338 ]
Página 364
Capítulo 9
# class Meta:
# ordering = ('-name',)
# index_together = (('id', 'slug'),)
Você pode ler mais sobre a compatibilidade do módulo django-parler com Django
em https://django-parler.readthedocs.io/en/latest/compatibility.html.
@ admin.register (categoria)
classe CategoryAdmin ( TranslatableAdmin ):
list_display = ['nome', 'slug']
@ admin.register (Produto)
classe ProductAdmin ( TranslatableAdmin ):
list_display = ['nome', 'slug', 'preço',
'disponível', 'criado', 'atualizado']
list_filter = ['disponível', 'criado', 'atualizado']
list_editable = ['preço', 'disponível']
https://translate.googleusercontent.com/translate_f 323/504
16/12/2021 10:52 Sem título
Você adaptou o site de administração para funcionar com os novos modelos traduzidos.
Agora você pode sincronizar o banco de dados com as alterações do modelo que você fez.
[ 339 ]
Página 365
com o seguinte:
Esta é uma correção para um pequeno problema encontrado na versão django-parler que você está usando.
Essa alteração é necessária para evitar que a migração falhe ao aplicá-la.
Este problema está relacionado à criação de traduções para campos existentes no modelo e
provavelmente será corrigido nas versões mais novas do django-parler .
[ 340 ]
https://translate.googleusercontent.com/translate_f 324/504
16/12/2021 10:52 Sem título
Página 366
Capítulo 9
Figura 9.10: O formulário de edição de categoria, incluindo guias de idioma adicionadas por django-parler
Certifique-se de preencher um nome e slug para todas as categorias existentes. Além disso, adicione
uma tradução para o espanhol para cada um deles e clique no botão SALVAR . Certificar-se
que você salve as alterações antes de alternar a guia ou você irá perdê-las.
[ 341 ]
https://translate.googleusercontent.com/translate_f 325/504
16/12/2021 10:52 Sem título
Página 367
Vamos dar uma olhada em como você pode recuperar e consultar os campos de tradução. Para obter o
objeto com campos traduzíveis traduzidos em um idioma específico, você pode usar o do Django
função activate () , como segue:
>>> product.name
'Té verde'
Outra maneira de fazer isso é usando o gerenciador de language () fornecido por django-
parler, como segue:
>>> product.name
'Chá verde'
Quando você acessa campos traduzidos, eles são resolvidos usando o idioma atual.
Você pode definir um idioma atual diferente para um objeto para acessar aquele específico
tradução, como segue:
>>> product.name
'Té verde'
>>> product.get_current_language ()
'es'
Vamos adaptar as visualizações do catálogo de produtos. Edite o arquivo views.py do aplicativo da loja
e, na visão product_list , encontre a seguinte linha:
[ 342 ]
Página 368
https://translate.googleusercontent.com/translate_f 326/504
16/12/2021 10:52 Sem título
Capítulo 9
idioma = solicitação.LANGUAGE_CODE
categoria = get_object_or_404 (categoria,
traduções__language_code = idioma,
Translations__slug = category_slug )
idioma = solicitação.LANGUAGE_CODE
product = get_object_or_404 (Produto,
id = id,
traduções__language_code = idioma,
Translations__slug = lesma,
disponível = verdadeiro)
[ 343 ]
Página 369
https://translate.googleusercontent.com/translate_f 327/504
16/12/2021 10:52 Sem título
http://127.0.0.1:8000/es/2/te-rojo/ , enquanto em inglês, o URL é
http://127.0.0.1:8000/en/2/red-tea/ . Se você navegar para um detalhe do produto
página, você verá o URL traduzido e o conteúdo do idioma selecionado,
conforme mostrado no seguinte exemplo:
Se você quiser saber mais sobre django-parler , você pode encontrar a versão completa
documentação em https://django-parler.readthedocs.io/en/latest/.
Você aprendeu como traduzir o código Python, modelos, padrões de URL e modelo
Campos. Para concluir o processo de internacionalização e localização, você precisa usar
formatação localizada para datas, horas e números também.
Localização de formato
Dependendo da localidade do usuário, você pode desejar exibir datas, horas e números
em diferentes formatos. A formatação localizada pode ser ativada alterando o USE_L10N
configuração como True no arquivo settings.py do seu projeto.
Quando USE_L10N estiver habilitado, o Django tentará usar um formato específico de local sempre que
ele produz um valor em um modelo. Você pode ver que os números decimais no inglês
versão do seu site são exibidos com um separador de pontos para casas decimais, enquanto
na versão em espanhol, eles são exibidos com uma vírgula. Isso é devido ao
formatos de locale especificados para o locale es pelo Django. Você pode dar uma olhada no
Configuração de formatação em espanhol em https://github.com/django/django/blob/
stable / 3.0.x / django / conf / locale / es / Formats.py.
[ 344 ]
Página 370
Capítulo 9
Normalmente, você definirá a configuração USE_L10N para True e deixará o Django aplicar o formato
localização para cada local. No entanto, pode haver situações em que você não
deseja usar valores localizados. Isso é especialmente relevante ao gerar JavaScript
ou JSON que deve fornecer um formato legível por máquina.
Django oferece um modelo de tag {% localize%} que permite que você ligue / desligue
localização para fragmentos de modelo. Isso lhe dá controle sobre a formatação localizada.
Você terá que carregar as tags l10n para poder usar esta tag de modelo. A seguir
é um exemplo de como ativar e desativar a localização em um modelo:
https://translate.googleusercontent.com/translate_f 328/504
16/12/2021 10:52 Sem título
{% load l10n%}
{% localize em%}
{{ valor }}
{% endlocalize%}
{% localize off%}
{{ valor }}
{% endlocalize%}
{{valor | localizar}}
{{valor | desvincular}}
Você também pode criar arquivos de formato personalizado para especificar a formatação local. Você pode encontrar
mais informações sobre localização de formatos em https: //docs.djangoproject.
com / en / 3.0 / topics / i18n / formatting /.
[ 345 ]
Página 371
INSTALLED_APPS = [
# ...
'localflavor',
]
Você adicionará o campo do código postal dos Estados Unidos para que um campo válido dos Estados Unidos
o código postal é necessário para criar um novo pedido.
Edite o arquivo forms.py do aplicativo de pedidos e faça com que tenha a seguinte aparência:
https://translate.googleusercontent.com/translate_f 329/504
16/12/2021 10:52 Sem título
postal_code = USZipCodeField ()
classe Meta:
modelo = pedido
campos = ['first_name', 'last_name', 'email', 'address',
'postal_code', 'city']
[ 346 ]
Página 372
Capítulo 9
• Página de detalhes do produto : você exibirá uma lista de produtos que geralmente são
comprado com o produto fornecido. Isso será exibido como usuários que compraram
este também comprado X, Y, Z . Você precisa de uma estrutura de dados que permite armazenar
o número de vezes que cada produto foi comprado junto com o
produto sendo exibido.
• Página de detalhes do carrinho : Com base nos produtos que os usuários adicionam ao carrinho, você vai
para sugerir produtos que normalmente se compram com estes. Nisso
Nesse caso, a pontuação que você calcula para obter produtos relacionados deve ser agregada.
https://translate.googleusercontent.com/translate_f 330/504
16/12/2021 10:52 Sem título
Você usará o Redis para armazenar produtos que são comprados juntos. Lembrar
que você já usou o Redis no Capítulo 6 , Rastreando ações do usuário . Se você não tem
instalado o Redis ainda, você pode encontrar as instruções de instalação naquele capítulo.
[ 347 ]
Página 373
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 1
Essas são as configurações necessárias para estabelecer uma conexão com o servidor Redis. Crio
um novo arquivo dentro do diretório do aplicativo da loja e nomeie-o recomendado.py . Adicionar
o seguinte código para ele:
importar redis
das configurações de importação do django.conf
from .models import Product
# conectar ao redis
r = redis.Redis (host = settings.REDIS_HOST,
port = settings.REDIS_PORT,
db = settings.REDIS_DB)
https://translate.googleusercontent.com/translate_f 331/504
16/12/2021 10:52 Sem título
1,
with_id)
Esta é a classe de Recomendador que permitirá que você armazene compras de produtos e
recuperar sugestões de produtos para um determinado produto ou produtos.
[ 348 ]
Página 374
Capítulo 9
Agora você tem um método para armazenar e pontuar os produtos que foram comprados juntos.
Em seguida, você precisa de um método para recuperar os produtos que foram comprados juntos por um
lista de determinados produtos. Adicione o seguinte método Suggest_products_for () ao
Classe de recomendação :
https://translate.googleusercontent.com/translate_f 332/504
16/12/2021 10:52 Sem título
desc = True) [: max_results]
# remove a chave temporária
r.delete (tmp_key)
Suggested_products_ids = [int (id) para id em sugestões]
[ 349 ]
Página 375
• produtos : Esta é uma lista de objetos Produto para os quais obter recomendações.
Ele pode conter um ou mais produtos.
[ 350 ]
https://translate.googleusercontent.com/translate_f 333/504
16/12/2021 10:52 Sem título
Página 376
Capítulo 9
Para fins práticos, vamos também adicionar um método para limpar as recomendações. Adicionar
o seguinte método para a classe Recomendador :
src / redis-server
Abra outro shell e execute o seguinte comando para abrir o shell Python:
Certifique-se de ter pelo menos quatro produtos diferentes em seu banco de dados. Recuperar
quatro produtos diferentes por seus nomes:
>>> r = Recomendador ()
chá preto: chá vermelho (2), chá_de_chá (2), chá verde (1)
chá_pó: chá preto (2), chá vermelho (1), chá verde (1)
[ 351 ]
https://translate.googleusercontent.com/translate_f 334/504
16/12/2021 10:52 Sem título
Página 377
Você pode ver que o pedido de produtos recomendados é baseado em sua pontuação. Vamos
obtenha recomendações para vários produtos com pontuações agregadas:
Você pode ver que a ordem dos produtos sugeridos corresponde às pontuações agregadas.
Por exemplo, os produtos sugeridos para chá preto e chá vermelho são chá_pó ( 2 + 1 )
e chá verde ( 1 + 1 ).
Você verificou que seu algoritmo de recomendação funciona conforme o esperado. Vamos
agora exibe recomendações de produtos em seu site.
[ 352 ]
Página 378
https://translate.googleusercontent.com/translate_f 335/504
16/12/2021 10:52 Sem título
Capítulo 9
r = Recomendador ()
recomendações_produtos = r.suggest_produtos_for ([produto], 4)
{% if recommended_products%}
<div class = "recomendações">
<h3> {% trans "Pessoas que compraram isto também compraram"%} </h3>
{% for p in recommended_products%}
<div class = "item">
<a href="{{ p.get_absolute_url }}">
<img src = "{% if p.image%} {{p.image.url}} {% else%}
{% static "img / no_image.png"%} {% endif%} ">
</a>
<p> <a href="{{ p.get_absolute_url }}"> {{p.name}} </a> </p>
</div>
{% endfor%}
</div>
{% fim se %}
[ 353 ]
Página 379
https://translate.googleusercontent.com/translate_f 336/504
16/12/2021 10:52 Sem título
os produtos são exibidos abaixo do produto, conforme mostrado na seguinte captura de tela:
[ 354 ]
Página 380
Capítulo 9
coupon_apply_form = CouponApplyForm ()
r = Recomendador ()
cart_products = [item ['product'] para o item no carrinho]
recommended_products = r.suggest_products_for (cart_products,
max_results = 4)
https://translate.googleusercontent.com/translate_f 337/504
16/12/2021 10:52 Sem título
retornar render (pedido,
'cart / detail.html',
{'carrinho': carrinho,
'coupon_apply_form': coupon_apply_form,
'recommended_products': recommended_products })
{% if recommended_products%}
<div class = "recomendações cart">
<h3> {% trans "Pessoas que compraram isto também compraram"%} </h3>
{% for p in recommended_products%}
<div class = "item">
<a href="{{ p.get_absolute_url }}">
<img src = "{% if p.image%} {{p.image.url}} {% else%}
{% static "img / no_image.png"%} {% endif%} ">
</a>
<p> <a href="{{ p.get_absolute_url }}"> {{p.name}} </a> </p>
</div>
{% endfor%}
</div>
{% fim se %}
[ 355 ]
Página 381
https://translate.googleusercontent.com/translate_f 338/504
16/12/2021 10:52 Sem título
Resumo
Neste capítulo, você criou um sistema de cupons usando sessões. Você também aprendeu o
noções básicas de internacionalização e localização para projetos Django. Você marcou
código e strings de modelo para tradução, e você descobriu como gerar
e compilar arquivos de tradução. Você também instalou Rosetta em seu projeto para gerenciar
traduções por meio de uma interface de navegador. Você traduziu padrões de URL e você
criou um seletor de idioma para permitir aos usuários trocar o idioma do site. Então,
você usou django-parler para traduzir modelos e você usou django-localflavor
para validar campos de formulário localizados. Finalmente, você construiu um mecanismo de recomendação usando
Redis para recomendar produtos que geralmente são comprados juntos.
No próximo capítulo, você iniciará um novo projeto. Você vai construir um e-learning
plataforma com Django usando visualizações baseadas em classe e você criará um
sistema de gerenciamento de conteúdo.
[ 356 ]
Página 382
Construindo um
10
Plataforma de E-Learning
No capítulo anterior, você adicionou a internacionalização ao seu projeto de loja online.
Você também construiu um sistema de cupons usando sessões e uma recomendação de produto
motor usando Redis. Neste capítulo, você iniciará um novo projeto Django. Você irá
construir uma plataforma de e-learning com seu próprio sistema de gerenciamento de conteúdo ( CMS ).
https://translate.googleusercontent.com/translate_f 339/504
16/12/2021 10:52 Sem título
As plataformas
fornecer de aprendizagem
ferramentas online são
para gerar conteúdo comumflexibilidade
ótimo exemplo de aplicativos
em mente. onde você
Neste capítulo, precisa
você vai
aprender a construir a funcionalidade para instrutores criar cursos e gerenciar o
conteúdos dos cursos de forma versátil e eficiente.
[ 357 ]
Página 383
mkdir env
python3 -m venv env / educa
Você vai gerenciar o upload de imagens em seu projeto, então também precisa instalar
Almofada com o seguinte comando:
cd educa
INSTALLED_APPS = [
'course.apps.CoursesConfig',
'django.contrib.admin',
https://translate.googleusercontent.com/translate_f 340/504
16/12/2021 10:52 Sem título
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
O aplicativo de cursos agora está ativo para o projeto. Vamos definir os modelos para
cursos e conteúdos dos cursos.
[ 358 ]
Página 384
Capítulo 10
Sujeito 1
Curso 1
Módulo 1
Conteúdo 1 (imagem)
Conteúdo 2 (texto)
Módulo 2
Conteúdo 3 (texto)
Conteúdo 4 (arquivo)
Conteúdo 5 (vídeo)
...
classe Meta:
ordenação = ['título']
https://translate.googleusercontent.com/translate_f 341/504
16/12/2021 10:52 Sem título
subject = models.ForeignKey (Assunto,
related_name = 'cursos',
on_delete = models.CASCADE)
title = models.CharField (max_length = 200)
slug = models.SlugField (max_length = 200, unique = True)
visão geral = models.TextField ()
[ 359 ]
Página 385
classe Meta:
ordenação = ['-criado']
Estes são os modelos iniciais de Assunto , Curso e Módulo . Os campos do modelo de curso
são como segue:
Abra o shell e execute o seguinte comando para criar a migração inicial para este
aplicativo:
[ 360 ]
https://translate.googleusercontent.com/translate_f 342/504
16/12/2021 10:52 Sem título
Página 386
Capítulo 10
Em seguida, execute o seguinte comando para aplicar todas as migrações ao banco de dados:
Você deve ver a saída que inclui todas as migrações aplicadas, incluindo aquelas
de Django. A saída conterá a seguinte linha:
@ admin.register (Assunto)
classe SubjectAdmin (admin.ModelAdmin):
list_display = ['título', 'slug']
prepopulated_fields = {'slug': ('title',)}
@ admin.register (curso)
classe CourseAdmin (admin.ModelAdmin):
list_display = ['título', 'assunto', 'criado']
list_filter = ['criado', 'assunto']
search_fields = ['título', 'visão geral']
prepopulated_fields = {'slug': ('title',)}
inlines = [ModuleInline]
[ 361 ]
https://translate.googleusercontent.com/translate_f 343/504
16/12/2021 10:52 Sem título
Página 387
"modelo": "cursos.subjeto",
[ 362 ]
Página 388
https://translate.googleusercontent.com/translate_f 344/504
16/12/2021 10:52 Sem título
Capítulo 10
"pk": 1,
"Campos": {
"título": "Matemática",
"lesma": "matemática"
}
},
"modelo": "cursos.subjeto",
"pk": 2,
"Campos": {
"título": "Música",
"slug": "música"
},
{
"modelo": "cursos.subjeto",
"pk": 3,
"Campos": {
"título": "Física",
"lesma": "física"
}
},
"modelo": "cursos.subjeto",
"pk": 4,
"Campos": {
"título": "Programação",
"slug": "programação"
}
]
[ 363 ]
Página 389
https://translate.googleusercontent.com/translate_f 345/504
16/12/2021 10:52 Sem título
formato app.Model . Você também pode especificar o formato usando o sinalizador --format . Por
padrão, dumpdata produz os dados serializados para a saída padrão. No entanto, você
pode indicar um arquivo de saída usando o sinalizador --output . O sinalizador --indent permite que você
para especificar o recuo. Para obter mais informações sobre os parâmetros dumpdata , execute python
manage.py dumpdata --help .
Salve este dump em um arquivo de fixtures em um novo diretório / fixtures nos cursos
aplicativo usando os seguintes comandos:
Por padrão, o Django procura por arquivos no diretório / fixtures de cada aplicativo, mas
você pode especificar o caminho completo para o arquivo de fixação para o comando loaddata . Você
também pode usar a configuração FIXTURE_DIRS para dizer ao Django diretórios adicionais para olhar em
para acessórios.
As luminárias não são úteis apenas para configurar os dados iniciais, mas também para
fornecendo dados de amostra para seu aplicativo ou dados necessários para
seus testes.
Você pode ler sobre como usar acessórios para teste em https: // docs.
djangoproject.com/en/3.0/topics/testing/tools/#fixture-loading .
[ 364 ]
Página 390
Capítulo 10
https://translate.googleusercontent.com/translate_f 346/504
16/12/2021 10:52 Sem título
Este é o modelo de conteúdo . Um módulo contém vários conteúdos, então você define um
Campo ForeignKey que aponta para o modelo do Módulo . Você também configura uma relação genérica
para associar objetos de diferentes modelos que representam diferentes tipos de conteúdo.
Lembre-se de que você precisa de três campos diferentes para configurar uma relação genérica. Na tua
Modelo de conteúdo , são eles:
[ 365 ]
Página 391
Você vai usar um modelo diferente para cada tipo de conteúdo. Seus modelos de conteúdo
terá alguns campos comuns, mas eles serão diferentes nos dados reais que podem armazenar.
• Modelos abstratos : Úteis quando você deseja colocar algumas informações comuns
em vários modelos.
• Herança do modelo de múltiplas tabelas : aplicável quando cada modelo no
a hierarquia é considerada um modelo completo por si só.
https://translate.googleusercontent.com/translate_f 347/504
16/12/2021 10:52 Sem título
Modelos abstratos
Um modelo abstrato é uma classe base na qual você define os campos que deseja incluir
em todos os modelos infantis. Django não cria nenhuma tabela de banco de dados para modelos abstratos.
Uma tabela de banco de dados é criada para cada modelo filho, incluindo os campos herdados de
a classe abstrata e aquelas definidas no modelo filho.
Para marcar um modelo como abstrato, você precisa incluir abstract = True em sua classe Meta .
O Django reconhecerá que é um modelo abstrato e não criará uma tabela de banco de dados
para isso. Para criar modelos filhos, você só precisa criar uma subclasse do modelo abstrato.
classe Meta:
abstract = True
[ 366 ]
Página 392
Capítulo 10
Nesse caso, o Django criaria uma tabela apenas para o modelo de texto , incluindo o
campos de título , criado e corpo .
Modelos de proxy
https://translate.googleusercontent.com/translate_f 348/504
16/12/2021 10:52 Sem título
[ 367 ]
Página 393
Aqui, você define um modelo OrderedContent que é um modelo proxy para o conteúdo
modelo. Este modelo fornece uma ordem padrão para QuerySets e um adicional
método created_delta () . Ambos os modelos, Content e OrderedContent , operam
na mesma tabela de banco de dados, e os objetos são acessíveis por meio do ORM por meio
qualquer um dos modelos.
classe Meta:
abstract = True
https://translate.googleusercontent.com/translate_f 349/504
16/12/2021 10:52 Sem título
Neste código, você define um modelo abstrato denominado ItemBase . Portanto, você define
abstract = True em sua classe Meta .
[ 368 ]
Página 394
Capítulo 10
Neste modelo, você define os campos proprietário , título , criado e atualizado . Esses
campos comuns serão usados para todos os tipos de conteúdo.
O campo do proprietário permite que você armazene qual usuário criou o conteúdo. Desde este campo
é definido em uma classe abstrata, você precisa de um related_name diferente para cada sub-
modelo. Django permite que você especifique um espaço reservado para o nome da classe do modelo no
Atributo related_name como % (class) s . Ao fazer isso, related_name para cada filho
modelo será gerado automaticamente. Já que você usa '% (class) s_related' como
o related_name , o relacionamento reverso para os modelos filhos será text_related ,
file_related , image_related e video_related , respectivamente.
Cada modelo filho contém os campos definidos na classe ItemBase , além de seus
próprios campos. Uma tabela de banco de dados será criada para Texto , Arquivo , Imagem e Vídeo
modelos, respectivamente. Não haverá nenhuma tabela de banco de dados associada ao ItemBase
modelo, por se tratar de um modelo abstrato.
Edite o modelo de conteúdo que você criou anteriormente e modifique seu campo content_type ,
do seguinte modo:
Você adiciona um argumento limit_choices_to para limitar os objetos ContentType que podem ser
usado para a relação genérica. Use a pesquisa de campo model__in para filtrar a consulta
aos objetos ContentType com um atributo de modelo que é 'texto' , 'vídeo' , 'imagem' ,
https://translate.googleusercontent.com/translate_f 350/504
16/12/2021 10:52 Sem título
ou 'arquivo' .
Vamos criar uma migração para incluir os novos modelos que você adicionou. Execute o
seguinte comando da linha de comando:
[ 369 ]
Página 395
Você precisa de um campo que permite definir uma ordem para os objetos. Um jeito facil
especificar uma ordem para objetos usando campos Django existentes é adicionando um
PositiveIntegerField para seus modelos. Usando números inteiros, você pode facilmente especificar
a ordem dos objetos. Você pode criar um campo de pedido personalizado que herda de
PositiveIntegerField e fornece comportamento adicional.
Existem duas funcionalidades relevantes que você irá construir em seu campo de pedido:
• Atribuir automaticamente um valor de pedido quando nenhum pedido específico for fornecido :
Ao salvar um novo objeto sem ordem específica, seu campo deve
atribuir automaticamente o número que vem após o último pedido existente
objeto. Se houver dois objetos com ordem 1 e 2 respectivamente, ao salvar
um terceiro objeto, você deve atribuir automaticamente a ordem 3 a ele, se não houver
o pedido foi fornecido.
[ 370 ]
https://translate.googleusercontent.com/translate_f 351/504
16/12/2021 10:52 Sem título
Página 396
Capítulo 10
[ 371 ]
https://translate.googleusercontent.com/translate_f 352/504
16/12/2021 10:52 Sem título
Página 397
Você pode encontrar mais informações sobre como escrever campos de modelo personalizado em https: // docs.
djangoproject.com/en/3.0/howto/custom-model-fields/ .
[ 372 ]
Página 398
https://translate.googleusercontent.com/translate_f 353/504
16/12/2021 10:52 Sem título
Capítulo 10
Você nomeia o novo pedido de campo e especifica que o pedido é calculado com
em relação ao curso definindo for_fields = ['curso'] . Isso significa que o
o pedido de um novo módulo será atribuído ao adicionar 1 ao último módulo do mesmo
Objeto do curso .
Agora, você pode editar o método __str __ () do modelo de módulo para incluir sua ordem,
do seguinte modo:
O conteúdo do módulo também precisa seguir uma ordem específica. Adicionar um campo OrderField
ao modelo de conteúdo , da seguinte maneira:
Desta vez, você especifica que o pedido é calculado em relação ao campo do módulo .
Finalmente, vamos adicionar uma ordem padrão para ambos os modelos. Adicione a seguinte classe Meta
para os modelos de Módulo e Conteúdo :
[ 373 ]
Página 399
https://translate.googleusercontent.com/translate_f 354/504
16/12/2021 10:52 Sem título
classe Meta:
pedido = ['pedido']
classe Meta:
pedido = ['pedido']
Vamos criar uma nova migração de modelo que reflete os novos campos do pedido. Abra a concha
e execute o seguinte comando:
Você está tentando adicionar um campo não anulável 'pedido' ao conteúdo sem
um padrão; não podemos fazer isso (o banco de dados precisa de algo para preencher
linhas existentes).
1) Forneça um padrão único agora (será definido em todas as linhas existentes com
um valor nulo para esta coluna)
2) Saia e deixe-me adicionar um padrão em models.py
[ 374 ]
Página 400
Capítulo 10
Django está lhe dizendo que você deve fornecer um valor padrão para o novo pedido
campo para linhas existentes no banco de dados. Se o campo tivesse null = True , ele aceitaria
valores nulos e o Django criaria a migração automaticamente em vez de perguntar
para um valor padrão. Você pode especificar um valor padrão ou cancelar a migração e
adicione um atributo padrão ao campo do pedido no arquivo models.py antes de criar
a migração.
Insira 1 e pressione Enter para fornecer um valor padrão para os registros existentes. Você verá
a seguinte saída:
https://translate.googleusercontent.com/translate_f 355/504
16/12/2021 10:52 Sem título
Por favor, insira o valor padrão agora, como Python válido
Os módulos datetime e django.utils.timezone estão disponíveis, para que você possa
faça, por exemplo, timezone.now
>>>
Insira 0 para que este seja o valor padrão para os registros existentes e pressione Enter . Django
solicitará um valor padrão para o modelo do módulo também. Escolha a primeira opção
e insira 0 como o valor padrão novamente. Finalmente, você verá uma saída semelhante a
o seguinte:
Vamos testar seu novo campo. Abra o shell com o seguinte comando:
[ 375 ]
Página 401
Você criou um curso no banco de dados. Agora, você adicionará módulos ao curso
e veja como seu pedido é calculado automaticamente. Você cria um módulo inicial e
verifique seu pedido:
>>> m1.order
OrderField define seu valor como 0 , pois este é o primeiro objeto Módulo criado para o
dado curso. Você, crie um segundo módulo para o mesmo curso:
https://translate.googleusercontent.com/translate_f 356/504
16/12/2021 10:52 Sem título
>>> m2.order
1
>>> m3.order
5
>>> m4.order
6
O pedido deste módulo foi definido automaticamente. Seu campo OrderField faz
não garante que todos os valores do pedido sejam consecutivos. No entanto, respeita os existentes
valores do pedido e sempre atribui o próximo pedido com base no pedido mais alto existente.
[ 376 ]
Página 402
Capítulo 10
>>> m5.order
Parabéns! Você criou com êxito seu primeiro campo de modelo personalizado.
Criação de um CMS
Agora que você criou um modelo de dados versátil, irá construir o CMS.
O CMS permitirá que os instrutores criem cursos e gerenciem seus conteúdos. Você
precisa fornecer a seguinte funcionalidade:
https://translate.googleusercontent.com/translate_f 357/504
16/12/2021 10:52 Sem título
urlpatterns = [
caminho ('accounts / login /', auth_views.LoginView.as_view (),
nome = 'login'),
caminho ('accounts / logout /', auth_views.LogoutView.as_view (),
nome = 'logout'),
caminho ('admin /', admin.site.urls),
]
[ 377 ]
Página 403
modelos/
base.html
cadastro/
login.html
logado_out.html
{% load static%}
<! DOCTYPE html>
<html>
<head>
<meta charset = "utf-8" />
<title> {% block title%} Educa {% endblock%} </title>
<link href = "{% static" css / base.css "%}" rel = "stylesheet">
</head>
<body>
<div id = "header">
<a href="/" class="logo"> Educa </a>
<ul class = "menu">
{% if request.user.is_authenticated%}
<li> <a href="{% url "logout" %}"> Sair </a> </li>
{% senão %}
<li> <a href="{% url "login" %}"> Faça login </a> </li>
{% fim se %}
</ul>
</div>
https://translate.googleusercontent.com/translate_f 358/504
16/12/2021 10:52 Sem título
<div id = "content">
{% block content%}
{% endblock%}
</div>
[ 378 ]
Página 404
Capítulo 10
</script>
</body>
</html>
Este é o modelo básico que será estendido pelo restante dos modelos. Nisso
modelo, você define os seguintes blocos:
• título : o bloco para outros modelos para adicionar um título personalizado para cada página.
• conteúdo : o bloco principal de conteúdo. Todos os modelos que estendem a base
o modelo deve adicionar conteúdo a este bloco.
• domready : Localizado dentro da função $ (document) .ready () do jQuery. Isto
permite que você execute código quando o Document Object Model ( DOM ) tem
terminou de carregar.
{% extends "base.html"%}
{% block content%}
<h1> Login </h1>
<div class = "module">
{% if form.errors%}
<p> Seu nome de usuário e senha não correspondem. Por favor, tente novamente. </
p>
{% senão %}
<p> Por favor, use o seguinte formulário para fazer o login: </p>
{% fim se %}
<div class = "login-form">
<form action = "{% url 'login'%}" method = "post">
{{form.as_p}}
{% csrf_token%}
https://translate.googleusercontent.com/translate_f 359/504
16/12/2021 10:52 Sem título
<input type = "hidden" name = "next" value = "{{next}}" />
<p> <input type = "submit" value = "Log-in"> </p>
</form>
</div>
</div>
{% endblock%}
[ 379 ]
Página 405
{% extends "base.html"%}
{% block content%}
<h1> Desconectado </h1>
<div class = "module">
<p> Você foi desconectado com sucesso.
Você pode <a href="{% url "login" %}"> fazer login novamente </a>. </p>
</div>
{% endblock%}
[ 380 ]
https://translate.googleusercontent.com/translate_f 360/504
16/12/2021 10:52 Sem título
Página 406
Capítulo 10
Django vem com vários mixins que fornecem funcionalidade adicional para sua classe-
visualizações baseadas. Você pode aprender mais sobre mixins em https: //docs.djangoproject.
com / en / 3.0 / topics / class-based-views / mixins /.
Você vai criar uma classe mixin que inclui um comportamento comum e usá-lo
para as visualizações do curso. Edite o arquivo views.py do aplicativo de cursos e modifique
da seguinte maneira:
[ 381 ]
https://translate.googleusercontent.com/translate_f 361/504
16/12/2021 10:52 Sem título
Página 407
Neste código, você cria as OwnerMixin e OwnerEditMixin mixins. Você vai usar
esses mixins junto com ListView , CreateView , UpdateView e DeleteView
visualizações fornecidas por Django. OwnerMixin implementa o método get_queryset () ,
que é usado pelas visualizações para obter o QuerySet base. Seu mixin irá substituir isso
método para filtrar objetos pelo atributo proprietário para recuperar objetos que pertencem ao
usuário atual ( request.user ).
[ 382 ]
Página 408
https://translate.googleusercontent.com/translate_f 362/504
16/12/2021 10:52 Sem título
Capítulo 10
O comportamento padrão para este método é salvar a instância (para formulários de modelo) e
redirecionando o usuário para success_url . Você substitui este método para automaticamente
defina o usuário atual no atributo de proprietário do objeto que está sendo salvo. Ao fazê-lo,
você define o proprietário de um objeto automaticamente quando ele é salvo.
Sua classe OwnerMixin pode ser usada para visualizações que interagem com qualquer modelo que
contém um atributo de proprietário .
Você também define uma classe OwnerCourseMixin que herda OwnerMixin e fornece
os seguintes atributos para visualizações filhas:
• modelo : o modelo usado para QuerySets; ele é usado por todas as visualizações.
[ 383 ]
Página 409
Como você pode ver, existem quatro permissões diferentes para cada modelo: pode ver , pode
adicionar , pode alterar e pode excluir . Depois de escolher as permissões para este grupo, clique em
o botão SALVAR .
Django cria permissões para modelos automaticamente, mas você também pode criar
permissões. Você aprenderá a criar permissões personalizadas no Capítulo 12, Construindo
uma API . Você pode ler mais sobre como adicionar permissões personalizadas em https: // docs.
djangoproject.com/en/3.0/topics/auth/customizing/#custom-permissions .
[ 384 ]
Página 410
Capítulo 10
https://translate.googleusercontent.com/translate_f 364/504
16/12/2021 10:52 Sem título
Os usuários herdam as permissões dos grupos aos quais pertencem, mas você também pode adicionar
permissões individuais para um único usuário usando o site de administração. Usuários que
ter is_superuser definido como True terá todas as permissões automaticamente.
[ 385 ]
Página 411
https://translate.googleusercontent.com/translate_f 365/504
16/12/2021 10:52 Sem título
permissão
agora especificada
acessível no atributo
apenas para com as permissões. Suas
usuáriospermission_required opiniões são
adequadas.
Vamos criar URLs para essas visualizações. Crie um novo arquivo dentro do aplicativo de cursos
diretório e nomeie- urls.py . Adicione o seguinte código a ele:
urlpatterns = [
caminho ('meu /',
views.ManageCourseListView.as_view (),
name = 'manage_course_list'),
caminho ('criar /',
views.CourseCreateView.as_view (),
name = 'course_create'),
caminho ('<pk> / edit /',
views.CourseUpdateView.as_view (),
nome = 'curso_edit'),
caminho ('<pk> / delete /',
views.CourseDeleteView.as_view (),
name = 'course_delete'),
]
Estes são os padrões de URL para listar, criar, editar e excluir visualizações do curso. Edite o
arquivo urls.py principal do projeto educa e inclui os padrões de URL dos cursos
aplicação, da seguinte forma:
[ 386 ]
Página 412
Capítulo 10
urlpatterns = [
caminho ('accounts / login /', auth_views.LoginView.as_view (),
nome = 'login'),
caminho ('accounts / logout /', auth_views.LogoutView.as_view (),
nome = 'logout'),
caminho ('admin /', admin.site.urls),
caminho ('curso /', incluir ('cursos.urls')),
]
Você precisa criar os modelos para essas visualizações. Crie os seguintes diretórios e
arquivos dentro do diretório / templates do aplicativo de cursos :
cursos/
gerir/
curso/
list.html
form.html
delete.html
{% extends "base.html"%}
https://translate.googleusercontent.com/translate_f 366/504
16/12/2021 10:52 Sem título
{% block content%}
<h1> Meus cursos </h1>
[ 387 ]
Página 413
Vamos criar o modelo que exibe o formulário para o curso de criação e atualização
Visualizações. Edite o modelo de cursos / manage / course / form.html e escreva o
seguinte código:
{% extends "base.html"%}
https://translate.googleusercontent.com/translate_f 367/504
16/12/2021 10:52 Sem título
{% block title%}
{% se objeto%}
Editar curso "{{object.title}}"
{% senão %}
Crie um novo curso
{% fim se %}
{% endblock%}
{% block content%}
<h1>
{% se objeto%}
Editar curso "{{object.title}}"
{% senão %}
[ 388 ]
Página 414
Capítulo 10
https://translate.googleusercontent.com/translate_f 368/504
16/12/2021 10:52 Sem título
[ 389 ]
Página 415
Em seguida, clique no link Editar do curso que acabou de criar. Você verá o formulário
novamente, mas desta vez você está editando um objeto de Curso existente em vez de criar um.
{% extends "base.html"%}
{% block content%}
<h1> Excluir curso "{{object.title}}" </h1>
[ 390 ]
https://translate.googleusercontent.com/translate_f 369/504
16/12/2021 10:52 Sem título
Página 416
Capítulo 10
Abra a lista de cursos no navegador e clique no link Excluir de seu curso. Você
deve ver a seguinte página de confirmação:
Os instrutores agora podem criar, editar e excluir cursos. Em seguida, você precisa fornecê-los
com um CMS para adicionar módulos de curso e seus conteúdos. Você vai começar gerenciando
módulos do curso.
[ 391 ]
https://translate.googleusercontent.com/translate_f 370/504
16/12/2021 10:52 Sem título
Página 417
Os conjuntos de formulários incluem um método is_valid () para validar todos os formulários de uma vez. Você também pode
fornecer dados iniciais para os formulários e especificar quantos formulários vazios adicionais para
exibição. Você pode aprender mais sobre os conjuntos de formulários emhttps://docs.djangoproject.com/
pt / 3.0 / topics / forms / formsets / e sobre modelos de formsets emhttps: // docs.
djangoproject.com/en/3.0/topics/forms/modelforms/#model-formsets .
Uma vez que um curso é dividido em um número variável de módulos, faz sentido
use formsets para gerenciá-los. Crie um arquivo forms.py no aplicativo de cursos
diretório e adicione o seguinte código a ele:
• can_delete : Se você definir isso como True , Django irá incluir um campo Booleano para
cada formulário que será renderizado como uma entrada de caixa de seleção. Permite que você marque
os objetos que você deseja excluir.
[ 392 ]
Página 418
https://translate.googleusercontent.com/translate_f 371/504
16/12/2021 10:52 Sem título
Capítulo 10
[ 393 ]
Página 419
https://translate.googleusercontent.com/translate_f 372/504
16/12/2021 10:52 Sem título
e visualizações:
• TemplateResponseMixin : Este mixin se encarrega de renderizar os templates
e retornando uma resposta HTTP. Requer um atributo template_name
que indica o modelo a ser renderizado e fornece o render_to_
response () para passar um contexto e renderizar o template.
• Visão : A visão básica baseada em classe fornecida pelo Django.
• get_formset () : você define este método para evitar a repetição do código para construir
o formset. Você cria um objeto ModuleFormSet para o objeto de curso fornecido
com dados opcionais.
[ 394 ]
Página 420
Capítulo 10
https://translate.googleusercontent.com/translate_f 373/504
16/12/2021 10:52 Sem título
{% extends "base.html"%}
{% block title%}
Editar "{{course.title}}"
{% endblock%}
{% block content%}
<h1> Editar "{{course.title}}" </h1>
<div class = "module">
<h2> Módulos do curso </h2>
<form method = "post">
{{formset}}
{{formset.management_form}}
{% csrf_token%}
<input type = "submit" value = "Save modules">
</form>
</div>
{% endblock%}
[ 395 ]
Página 421
https://translate.googleusercontent.com/translate_f 374/504
16/12/2021 10:52 Sem título
Figura 10.9: A página de edição do curso, incluindo o formset para módulos do curso
[ 396 ]
Página 422
Capítulo 10
https://translate.googleusercontent.com/translate_f 375/504
16/12/2021 10:52 Sem título
[ 397 ]
Página 423
https://translate.googleusercontent.com/translate_f 376/504
16/12/2021 10:52 Sem título
# novos conteúdos
Content.objects.create (module = self.module,
item = obj)
return redirect ('module_content_list', self.module.id)
• get () : executado quando uma solicitação GET é recebida. Você constrói o modelo
formulário para a instância de Texto , Vídeo , Imagem ou Arquivo que está sendo atualizada.
Caso contrário, você não passa nenhuma instância para criar um novo objeto, pois self.obj
é Nenhum se nenhum ID for fornecido.
[ 398 ]
Página 424
Capítulo 10
• post () : executado quando uma solicitação POST é recebida. Você constrói o modelo
formulário, passando quaisquer dados e arquivos enviados para ele. Então, você valida isso. Se o
formulário é válido, você cria um novo objeto e atribui request.user como seu proprietário
antes de salvá-lo no banco de dados. Você verifica o parâmetro id . Se nenhum ID for
fornecido, você sabe que o usuário está criando um novo objeto em vez de atualizar um
existente. Se este for um novo objeto, você cria um objeto de conteúdo para o determinado
módulo e associar o novo conteúdo a ele.
{% extends "base.html"%}
{% block title%}
{% se objeto%}
Edite o conteúdo "{{object.title}}"
{% senão %}
https://translate.googleusercontent.com/translate_f 377/504
16/12/2021 10:52 Sem título
Adicionar novo conteúdo
{% fim se %}
{% endblock%}
{% block content%}
<h1>
[ 399 ]
Página 425
{% se objeto%}
Edite o conteúdo "{{object.title}}"
{% senão %}
Adicionar novo conteúdo
{% fim se %}
</h1>
<div class = "module">
<h2> Informações do curso </h2>
<form action = "" method = "post" enctype = "multipart / form-data">
{{form.as_p}}
{% csrf_token%}
<p> <input type = "submit" value = "Save content"> </p>
</form>
</div>
{% endblock%}
[ 400 ]
https://translate.googleusercontent.com/translate_f 378/504
16/12/2021 10:52 Sem título
Página 426
Capítulo 10
Não envie o formulário ainda. Se você tentar fazer isso, ele irá falhar porque você não definiu
o URL module_content_list ainda. Você vai criá-lo em breve.
Você também precisa de uma visão para excluir o conteúdo. Edite o arquivo views.py dos cursos
aplicativo e adicione o seguinte código:
[ 401 ]
https://translate.googleusercontent.com/translate_f 379/504
16/12/2021 10:52 Sem título
Página 427
[ 402 ]
Página 428
https://translate.googleusercontent.com/translate_f 380/504
16/12/2021 10:52 Sem título
Capítulo 10
{% extends "base.html"%}
{% block title%}
Módulo {{module.order | add: 1}}: {{module.title}}
{% endblock%}
{% block content%}
{% com curso = módulo.curso%}
<h1> Curso "{{course.title}}" </h1>
<div class = "contents">
<h3> Módulos </h3>
<ul id = "modules">
{% for m in course.modules.all%}
<li data-id = "{{m.id}}" {% if m == módulo%}
classe = "selecionado" {% endif%}>
<a href="{% url "module_content_list" m.id %}">
<span>
Módulo <span class = "order"> {{m.order | add: 1}} </span>
</span>
<br>
{{m.title}}
</a>
</li>
{% vazio %}
<li> Ainda não há módulos. </li>
{% endfor%}
</ul>
<p> <a href="{% url "course_module_update" course.id %}">
Editar módulos </a> </p>
</div>
<div class = "module">
<h2> Módulo {{module.order | add: 1}}: {{module.title}} </h2>
<h3> Conteúdo do módulo: </h3>
<div id = "module-contents">
{% para conteúdo em module.contents.all%}
<div data-id = "{{content.id}}">
{% with item = content.item%}
<p> {{item}} </p>
<a href="#"> Editar </a>
<form action = "{% url" module_content_delete "content.id
%} "
method = "post">
<input type = "submit" value = "Delete">
{% csrf_token%}
</form>
{% endwith%}
[ 403 ]
Página 429
</div>
{% vazio %}
<p> Este módulo ainda não tem conteúdo. </p>
https://translate.googleusercontent.com/translate_f 381/504
16/12/2021 10:52 Sem título
{% endfor%}
</div>
<h3> Adicionar novo conteúdo: </h3>
<ul class = "content-types">
<li> <a href = "{% url" module_content_create "module.id" text "
%} ">
Texto </a> </li>
<li> <a href = "{% url" module_content_create "module.id" imagem "
%} ">
Imagem </a> </li>
<li> <a href = "{% url" module_content_create "module.id" video "
%} ">
Vídeo </a> </li>
<li> <a href = "{% url" module_content_create "module.id" file "
%} ">
Arquivo </a> </li>
</ul>
</div>
{% endwith%}
{% endblock%}
Você quer saber qual tipo de objeto cada um dos objetos do item é: Texto , Vídeo ,
Imagem ou arquivo . Você precisa do nome do modelo para construir a URL para editar o objeto. além do mais
isso, você pode exibir cada item no modelo de forma diferente com base no tipo de
conteúdo é. Você pode obter o nome do modelo para um objeto da classe Meta do modelo por
acessando o atributo _meta do objeto . No entanto, o Django não permite o acesso
variáveis ou atributos começando com um sublinhado em modelos para evitar a recuperação
atributos privados ou chamada de métodos privados. Você pode resolver isso escrevendo um
filtro de modelo.
templatetags /
__init__.py
course.py
[ 404 ]
Página 430
Capítulo 10
register = template.Library ()
@ register.filter
def model_name (obj):
experimentar:
return obj._meta.model_name
https://translate.googleusercontent.com/translate_f 382/504
16/12/2021 10:52 Sem título
exceto AttributeError:
retornar nenhum
{% load curso%}
No código anterior, você exibe o nome do modelo do item no modelo e também usa
o nome do modelo para construir o link para editar o objeto.
O novo link permite que os usuários acessem o conteúdo do primeiro módulo do curso,
se houver algum.
[ 405 ]
Página 431
https://translate.googleusercontent.com/translate_f 383/504
16/12/2021 10:52 Sem título
Quando você clica em um módulo na barra lateral esquerda, seu conteúdo é exibido na janela principal
área. O modelo também inclui links para adicionar novo texto, vídeo, imagem ou conteúdo de arquivo para
o módulo sendo exibido.
[ 406 ]
Página 432
Capítulo 10
https://translate.googleusercontent.com/translate_f 384/504
16/12/2021 10:52 Sem título
pip install django-braces == 1.14.0
Você precisa de uma visualização que receba a nova ordem de IDs de módulo codificados em JSON.
Edite o arquivo views.py do aplicativo de cursos e adicione o seguinte código a ele:
[ 407 ]
Página 433
Você pode construir uma visão semelhante para ordenar o conteúdo de um módulo. Adicione o seguinte código
para o arquivo views.py :
https://translate.googleusercontent.com/translate_f 385/504
16/12/2021 10:52 Sem título
<script src = "https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/
jquery-ui.min.js "> </script>
Você carrega a biblioteca jQuery UI logo abaixo da estrutura jQuery. Em seguida, edite o
cursos / manage / module / content_list.html template e adicione o seguinte código
para ele na parte inferior do modelo:
{% block domready%}
$ ('# módulos'). sortable ({
parar: função (evento, ui) {
modules_order = {};
$ ('# módulos'). children (). each (function () {
// atualiza o campo do pedido
[ 408 ]
Página 434
Capítulo 10
$ .ajax ({
tipo: 'POST',
url: '{% url "content_order"%}',
contentType: 'application / json; charset = utf-8 ',
dataType: 'json',
dados: JSON.stringify (contents_order),
});
}
});
{% endblock%}
Você define um elemento classificável para a lista de módulos na barra lateral e um diferente
https://translate.googleusercontent.com/translate_f 386/504
16/12/2021 10:52 Sem título
para a lista de conteúdo do módulo. Ambos funcionam de maneira semelhante.
[ 409 ]
Página 435
1. Você define um elemento classificável para o elemento HTML dos módulos . Lembrar
que você usa #modules , uma vez que jQuery usa notação CSS para seletores.
2. Você especifica uma função para o evento de parada . Este evento é acionado todas as vezes
o usuário termina de classificar um elemento.
O elemento classificável para ordenar o conteúdo do módulo é bastante semelhante a este. Volte
para o seu navegador e recarregue a página. Agora você poderá clicar e arrastar ambos
módulos e seus conteúdos para reordená-los como o exemplo a seguir:
[ 410 ]
https://translate.googleusercontent.com/translate_f 387/504
16/12/2021 10:52 Sem título
Página 436
Capítulo 10
Resumo
Neste capítulo, você aprendeu como usar acessórios de fixação para fornecer dados iniciais para modelos. Por
usando a herança do modelo, você criou um sistema versátil para gerenciar diferentes tipos
de conteúdo para os módulos do curso. Você implementou um campo de modelo personalizado para solicitar
objetos. Você também descobriu como usar visualizações e mixins baseados em classes. Você trabalhou
com grupos e permissões para restringir o acesso às suas visualizações. Finalmente, você usou
conjuntos de formulários para gerenciar os módulos do curso, e você construiu uma funcionalidade de arrastar e soltar
com jQuery UI para reordenar os módulos e seus conteúdos.
No próximo capítulo, você criará um sistema de registro de alunos. Você também vai
renderizar diferentes tipos de conteúdo, e você aprenderá como trabalhar com os
estrutura de cache.
[ 411 ]
https://translate.googleusercontent.com/translate_f 388/504
16/12/2021 10:52 Sem título
Página 438
437
Renderização e
11
Conteúdo de cache
No capítulo anterior, você usou herança de modelo e relações genéricas para criar
modelos flexíveis de conteúdo do curso. Você implementou um campo de modelo personalizado e
construiu um sistema de gerenciamento de curso usando visualizações baseadas em classe. Finalmente, você criou um
Funcionalidade arrastar e soltar baseada em AJAX para solicitar os módulos do curso e seus conteúdos.
Neste capítulo, você construirá a funcionalidade para acessar o conteúdo do curso, criar
um sistema de registro de alunos e gerenciar a inscrição de alunos nos cursos. Você
também aprenderá como armazenar dados em cache usando a estrutura de cache do Django.
Vamos começar criando um catálogo de cursos para os alunos navegarem nos cursos existentes e
inscreva-se neles.
[ 413 ]
Página 439
https://translate.googleusercontent.com/translate_f 389/504
16/12/2021 10:52 Sem título
Exibindo cursos
Para o seu catálogo de cursos, você deve criar as seguintes funcionalidades:
[ 414 ]
Página 440
Capítulo 11
Vamos criar uma visão detalhada para exibir uma visão geral do curso único. Adicione o seguinte
código para o arquivo views.py :
https://translate.googleusercontent.com/translate_f 390/504
16/12/2021 10:52 Sem título
de django.views.generic.detail import DetailView
Esta visão é herdada do DetailView genérico fornecido pelo Django. Você especifica
os atributos model e template_name . O DetailView do Django espera um primário
key ( pk ) ou parâmetro de URL slug para recuperar um único objeto para o modelo fornecido. O
view renderiza o template especificado em template_name , incluindo o objeto Curso
no objeto de variável de contexto do modelo .
Edite o arquivo urls.py principal do projeto educa e adicione o seguinte padrão de URL
para isso:
urlpatterns = [
# ...
path ('', CourseListView.as_view (), name = 'course_list'),
]
[ 415 ]
Página 441
curso/
list.html
detail.html
https://translate.googleusercontent.com/translate_f 391/504
16/12/2021 10:52 Sem título
o seguinte código:
{% extends "base.html"%}
{% block title%}
{% se assunto%}
cursos de {{subject.title}}
{% senão %}
Todos os cursos
{% fim se %}
{% endblock%}
{% block content%}
<h1>
{% se assunto%}
cursos de {{subject.title}}
{% senão %}
Todos os cursos
{% fim se %}
</h1>
<div class = "contents">
<h3> Assuntos </h3>
<ul id = "modules">
<li {% if not subject%} class = "selecionado" {% endif%}>
<a href="{% url "course_list" %}"> Todos </a>
</li>
{% para s em assuntos%}
<li {% if subject == s%} class = "selected" {% endif%}>
<a href="{% url "course_list_subject" s.slug %}">
{{s.title}}
<br> <span> {{s.total_courses}} cursos </span>
</a>
</li>
{% endfor%}
</ul>
[ 416 ]
Página 442
Capítulo 11
</div>
<div class = "module">
{% para curso em cursos%}
{% with subject = course.subject%}
<h3>
<a href="{% url "course_detail" course.slug %}">
{{ título do curso }}
</a>
</h3>
p
<a href="{% url "course_list_subject" subject.slug %}"> {{
assunto}} </a>.
Módulos de {{course.total_modules}}.
Instrutor: {{course.owner.get_full_name}}
</p>
{% endwith%}
{% endfor%}
</div>
{% endblock%}
https://translate.googleusercontent.com/translate_f 392/504
16/12/2021 10:52 Sem título
Este é o modelo para listar os cursos disponíveis. Você cria uma lista HTML para
exibir todos os objetos Subject e construir um link para a URL course_list_subject para
cada um deles. Você adiciona uma classe HTML selecionada para destacar o assunto atual se um
o assunto é selecionado. Você itera sobre cada objeto de curso , exibindo o número total
dos módulos e o nome do instrutor.
[ 417 ]
Página 443
A barra lateral esquerda contém todos os assuntos, incluindo o número total de cursos para cada
deles. Você pode clicar em qualquer assunto para filtrar os cursos exibidos.
{% extends "base.html"%}
{% block title%}
{{object.title}}
{% endblock%}
{% block content%}
{% with subject = object.subject%}
<h1>
{{object.title}}
</h1>
<div class = "module">
<h2> Visão geral </h2>
p
<a href="{% url "course_list_subject" subject.slug %}">
{{subject.title}} </a>.
Módulos {{object.modules.count}}.
Instrutor: {{object.owner.get_full_name}}
</p>
{{object.overview | quebras de linha}}
</div>
https://translate.googleusercontent.com/translate_f 393/504
16/12/2021 10:52 Sem título
{% endwith%}
{% endblock%}
Neste modelo, você exibe a visão geral e os detalhes de um único curso. Aberto
http://127.0.0.1:8000/ em seu navegador e clique em um dos cursos. Você
deve ver uma página com a seguinte estrutura:
[ 418 ]
Página 444
Capítulo 11
Você criou uma área pública para exibição de cursos. Em seguida, você precisa permitir que os usuários
para se inscrever como alunos e se inscrever em cursos.
INSTALLED_APPS = [
# ...
'students.apps.StudentsConfig',
]
Esta é a visualização que permite que os alunos se inscrevam em seu site. Você usa o genérico
CreateView , que fornece a funcionalidade para criar objetos de modelo. Esta vista
requer os seguintes atributos:
[ 419 ]
Página 445
O método form_valid () é executado quando dados de formulário válidos são postados. Tem
para retornar uma resposta HTTP. Você substitui este método para fazer o login do usuário depois que ele
se inscreveu com sucesso.
urlpatterns = [
caminho ('registrar /',
views.StudentRegistrationView.as_view (),
name = 'student_registration'),
]
urlpatterns = [
# ...
caminho ( 'estudantes /', incluem ( 'students.urls')),
]
modelos/
alunos /
aluna/
registration.html
https://translate.googleusercontent.com/translate_f 395/504
16/12/2021 10:52 Sem título
código para ele:
{% extends "base.html"%}
{% block title%}
Inscrever-se
[ 420 ]
Página 446
Capítulo 11
{% endblock%}
{% block content%}
<h1>
Inscrever-se
</h1>
<div class = "module">
<p> Insira seus dados para criar uma conta: </p>
<form method = "post">
{{form.as_p}}
{% csrf_token%}
<p> <input type = "submit" value = "Create my account"> </p>
</form>
</div>
{% endblock%}
[ 421 ]
https://translate.googleusercontent.com/translate_f 396/504
16/12/2021 10:52 Sem título
Página 447
Inscrever-se em cursos
Depois que os usuários criam uma conta, eles devem ser capazes de se inscrever nos cursos. Em ordem
para armazenar inscrições, você precisa criar uma relação muitos-para-muitos entre o
Modelos de cursos e usuários .
No shell, execute o seguinte comando para criar uma migração para essa mudança:
Você deve ver uma saída que termina com a seguinte linha:
Agora você pode associar os alunos aos cursos nos quais estão matriculados. Vamos
criar a funcionalidade para os alunos se inscreverem nos cursos.
Crie um novo arquivo dentro do diretório do aplicativo dos alunos e nomeie-o forms.py .
Adicione o seguinte código a ele:
[ 422 ]
https://translate.googleusercontent.com/translate_f 397/504
16/12/2021 10:52 Sem título
Página 448
Capítulo 11
Você usará este formulário para que os alunos se inscrevam nos cursos. O campo do curso é
para o curso em que o usuário será matriculado; portanto, é um ModelChoiceField .
Você usa um widget HiddenInput porque não vai mostrar este campo para
o usuário. Você vai usar este formulário na visualização CourseDetailView para exibir
um botão para se inscrever.
Edite o arquivo urls.py do aplicativo dos alunos e adicione o seguinte padrão de URL
para isso:
[ 423 ]
Página 449
https://translate.googleusercontent.com/translate_f 398/504
16/12/2021 10:52 Sem título
Vamos adicionar o formulário do botão de inscrição à página de visão geral do curso. Edite as visualizações.
arquivo py do aplicativo de cursos e modificar CourseDetailView para torná-lo parecido
do seguinte modo:
Este é o botão para se inscrever em cursos. Se o usuário for autenticado, você exibe
o botão de inscrição, incluindo o formulário oculto que aponta para o aluno_
URL matricula_curso . Se o usuário não estiver autenticado, você exibe um link para se registrar
na plataforma.
[ 424 ]
Página 450
Capítulo 11
https://translate.googleusercontent.com/translate_f 399/504
16/12/2021 10:52 Sem título
Botão AGORA colocado abaixo da visão geral do curso, como segue:
Figura 11.4: A página de visão geral do curso, incluindo um botão INSCREVA-SE AGORA
[ 425 ]
Página 451
https://translate.googleusercontent.com/translate_f 400/504
16/12/2021 10:52 Sem título
context = super (). get_context_data (** kwargs)
# obter o objeto do curso
curso = self.get_object ()
if 'module_id' em self.kwargs:
# obter módulo atual
contexto ['módulo'] = curso.modules.get (
id = self.kwargs ['module_id'])
senão:
# obter o primeiro módulo
contexto ['módulo'] = curso.modules.all () [0]
contexto de retorno
[ 426 ]
Página 452
Capítulo 11
curso/
detail.html
list.html
{% extends "base.html"%}
{% block content%}
<h1> Meus cursos </h1>
https://translate.googleusercontent.com/translate_f 401/504
16/12/2021 10:52 Sem título
<div class = "course-info">
<h3> {{course.title}} </h3>
<p> <a href="{% url "student_course_detail" course.id %}">
Acesse conteúdos </a> </p>
</div>
{% vazio %}
p
Você ainda não está inscrito em nenhum curso.
<a href="{% url "course_list" %}"> Navegar nos cursos </a>
para se inscrever em um curso.
</p>
{% endfor%}
</div>
{% endblock%}
Este modelo exibe os cursos nos quais o aluno está matriculado. Lembre-se disso
quando um novo aluno se registrar com sucesso na plataforma, ele será redirecionado
para o URL student_course_list . Também vamos redirecionar os alunos para este URL quando
eles fazem login na plataforma.
[ 427 ]
Página 453
Esta é a configuração usada pelo módulo de autenticação para redirecionar o aluno após uma
login se nenhum próximo parâmetro estiver presente na solicitação. Após um login bem-sucedido,
um aluno será redirecionado para a URL student_course_list para ver os cursos
em que estão inscritos.
{% extends "base.html"%}
{% block title%}
{{object.title}}
{% endblock%}
{% block content%}
<h1>
{{module.title}}
</h1>
<div class = "contents">
<h3> Módulos </h3>
<ul id = "modules">
{% para m em object.modules.all%}
<li data-id = "{{m.id}}" {% if m == módulo%}
classe = "selecionado" {% endif%}>
<a href = "{% url" student_course_detail_module "object.id
m.id%} ">
https://translate.googleusercontent.com/translate_f 402/504
16/12/2021 10:52 Sem título
<span>
Módulo <span class = "order"> {{m.order | add: 1}} </span>
</span>
<br>
{{m.title}}
</a>
</li>
{% vazio %}
<li> Ainda não há módulos. </li>
{% endfor%}
</ul>
</div>
<div class = "module">
{% para conteúdo em module.contents.all%}
{% with item = content.item%}
<h2> {{item.title}} </h2>
{{item.render}}
{% endwith%}
{% endfor%}
</div>
{% endblock%}
[ 428 ]
Página 454
Capítulo 11
https://translate.googleusercontent.com/translate_f 403/504
16/12/2021 10:52 Sem título
contente/
text.html
file.html
image.html
video.html
Este é o modelo para renderizar o conteúdo de texto. O filtro do modelo de quebra de linha substitui
quebras de linha em texto simples com quebras de linha HTML.
[ 429 ]
Página 455
<p> <a href="{{ item.file.url }}" class="button"> Baixar arquivo </a> </p>
Este é o modelo para renderizar arquivos. Você gera um link para baixar o arquivo.
Este é o modelo para renderizar imagens. Para arquivos carregados com ImageField e
FileField para funcionar, você precisa configurar seu projeto para servir arquivos de mídia com o
servidor de desenvolvimento.
Lembre-se de que MEDIA_URL é o URL base para servir arquivos de mídia carregados e
MEDIA_ROOT é o caminho local onde os arquivos estão localizados.
if settings.DEBUG:
urlpatterns + = static (settings.MEDIA_URL,
document_root = settings.MEDIA_ROOT)
Seu projeto agora está pronto para fazer upload e servir arquivos de mídia. O desenvolvimento do Django
servidor será responsável por servir os arquivos de mídia durante o desenvolvimento (ou seja, quando
a configuração DEBUG é definida como True ). Lembre-se de que o servidor de desenvolvimento não é
adequado para uso em produção. Você aprenderá como configurar um ambiente de produção
no Capítulo 14 , Going Live .
Você também deve criar um modelo para renderizar objetos de vídeo . Você vai usar django-
embed-video para incorporar conteúdo de vídeo. django-embed-video é um terceiro
Aplicativo Django que permite incorporar vídeos em seus modelos, a partir de fontes
https://translate.googleusercontent.com/translate_f 404/504
16/12/2021 10:52 Sem título
como YouTube ou Vimeo, simplesmente fornecendo seu URL público.
Instale o pacote com o seguinte comando:
[ 430 ]
Página 456
Capítulo 11
INSTALLED_APPS = [
# ...
'embed_video',
]
{% load embed_video_tags%}
{% video item.url "pequeno"%}
[ 431 ]
https://translate.googleusercontent.com/translate_f 405/504
16/12/2021 10:52 Sem título
Página 457
Excelente! Você criou uma interface comum para renderizar diferentes tipos de curso
conteúdo.
Django inclui um sistema de cache robusto que permite que você armazene dados em cache com diferentes
níveis de granularidade. Você pode armazenar em cache uma única consulta, a saída de uma visualização específica,
partes do conteúdo do modelo renderizado ou todo o seu site. Os itens são armazenados no cache
sistema por um tempo padrão. Você pode especificar o tempo limite padrão para dados em cache.
É assim que você geralmente usa a estrutura de cache quando seu aplicativo obtém
uma solicitação HTTP:
Você pode ler informações detalhadas sobre o sistema de cache do Django em https: // docs.
djangoproject.com/en/3.0/topics/cache/.
• backends.memcached.MemcachedCache ou backends.memcached.
PyLibMCCache : um back-end do Memcached. Memcached é um método rápido e eficiente
servidor de cache baseado em memória. O backend a ser usado depende do Memcached
Vinculações Python que você escolher.
• backends.db.DatabaseCache : Use o banco de dados como um sistema de cache.
[ 432 ]
https://translate.googleusercontent.com/translate_f 406/504
16/12/2021 10:52 Sem título
Página 458
Capítulo 11
Instalando Memcached
Você usará o back-end do Memcached. Memcached roda na memória e
recebe uma quantidade especificada de RAM. Quando a RAM alocada está cheia, o Memcached
começa a remover os dados mais antigos para armazenar novos dados.
./configure && make && make test && sudo make install
Se você estiver usando o macOS, pode instalar o Memcached com o pacote Homebrew
gerenciador usando o comando brew install memcached . Você pode baixar
Homebrew de https://brew.sh/ .
memcached -l 127.0.0.1:11211
Memcached será executado na porta 11211 por padrão. No entanto, você pode especificar um
host e porta usando a opção -l . Você pode encontrar mais informações sobre
Memcached em https://memcached.org .
Depois de instalar o Memcached, você deve instalar seus vínculos Python. Você consegue fazer isso
com o seguinte comando:
[ 433 ]
Página 459
https://translate.googleusercontent.com/translate_f 407/504
16/12/2021 10:52 Sem título
Configurações de cache
Django fornece as seguintes configurações de cache:
O sistema de cache para o projeto pode ser configurado usando a configuração CACHES .
Esta configuração permite que você especifique a configuração para vários caches. Cada cache
incluído no dicionário CACHES pode especificar os seguintes dados:
• KEY_FUNCTION : Uma string contendo um caminho pontilhado para um chamável que leva
um prefixo, versão e chave como argumentos e retorna uma chave de cache final.
• KEY_PREFIX : Um prefixo de string para todas as chaves de cache, para evitar colisões.
• LOCALIZAÇÃO : A localização do cache. Dependendo do back-end do cache, este
pode ser um diretório, um host e uma porta ou um nome para o back-end na memória.
CACHES = {
'padrão': {
'BACKEND': 'django.core.cache.backends.memcached.
MemcachedCache ',
'LOCALIZAÇÃO': '127.0.0.1:11211',
}
}
[ 434 ]
Página 460
Capítulo 11
Você está usando o back-end MemcachedCache . Você especifica sua localização usando o
endereço: notação da porta . Se você tiver várias instâncias do Memcached, pode usar
https://translate.googleusercontent.com/translate_f 408/504
16/12/2021 10:52 Sem título
uma lista para LOCATION .
Monitorando Memcached
Para monitorar o Memcached, você usará um pacote de terceiros chamado django-
status do memcache. Este aplicativo exibe estatísticas para suas instâncias do Memcached
no site de administração. Instale-o com o seguinte comando:
INSTALLED_APPS = [
# ...
'memcache_status',
]
Certifique-se de que o Memcached está em execução, inicie o servidor de desenvolvimento em outro shell
janela e abra http://127.0.0.1:8000/admin/ em seu navegador. Faça login no
site de administração usando um superusuário. Você deve ver o seguinte bloco no
página de índice do site de administração:
O bloco contém um gráfico de barras que mostra a carga do cache. A cor verde representa
cache livre, enquanto o vermelho indica espaço usado. Se você clicar no título da caixa, ele mostra
estatísticas detalhadas de sua instância do Memcached.
Você configurou o Memcached para o seu projeto e pode monitorá-lo. Vamos começar
caching data!
[ 435 ]
Página 461
Níveis de cache
Django fornece os seguintes níveis de cache, listados aqui em ordem crescente de
granularidade:
• API de cache de baixo nível : fornece a maior granularidade. Permite que você armazene em cache
consultas ou cálculos específicos.
https://translate.googleusercontent.com/translate_f 409/504
16/12/2021 10:52 Sem título
• Cache por site : o cache de nível mais alto. Ele armazena em cache todo o seu site.
Vamos dar uma olhada em como funciona a API de cache. Abra o shell com o comando
shell python manage.py e execute o seguinte código:
Você acessa o back-end do cache padrão e usa set (chave, valor, tempo limite) para armazenar
uma chave chamada 'musician' com um valor que é a string 'Django Reinhardt' para 20
segundos. Se você não especificar um tempo limite, o Django usa o tempo limite padrão especificado para
o back-end do cache na configuração CACHES . Agora, execute o seguinte código:
'Django Reinhardt'
[ 436 ]
Página 462
Capítulo 11
Nenhum valor é retornado neste momento. A chave de cache 'musician' expirou e o get ()
o método retorna None porque a chave não está mais no cache.
https://translate.googleusercontent.com/translate_f 410/504
16/12/2021 10:52 Sem título
Você irá armazenar em cache algumas consultas em suas visualizações. Edite o arquivo views.py do
aplicativo de cursos e adicione a seguinte importação:
subject = Subject.objects.annotate (
total_courses = Count ('cursos'))
Neste código, você tenta obter a chave all_students do cache usando o cache.
get () . Isso retorna None se a chave fornecida não for encontrada. Se nenhuma chave for encontrada (não em cache
ainda ou em cache, mas expirou), você executa a consulta para recuperar todos os objetos Subject
e seu número de cursos, e você armazena em cache o resultado usando cache.set () .
[ 437 ]
Página 463
https://translate.googleusercontent.com/translate_f 411/504
16/12/2021 10:52 Sem título
Dê uma olhada em Curr Items , que deve ser 1 . Isso mostra que há um item
atualmente armazenado no cache. Get Hits mostra quantos comandos get foram
bem-sucedido e Obter erros mostra as solicitações de obtenção de chaves que estão faltando.
O Miss Ratio é calculado usando ambos.
[ 438 ]
Página 464
Capítulo 11
Nesse caso, você também armazena em cache todos os cursos e cursos filtrados por assunto. Você usa
a chave de cache all_courses para armazenar todos os cursos se nenhum assunto for fornecido. Se houver um
assunto, você constrói a chave dinamicamente com f'subject_ {subject.id} _courses ' .
https://translate.googleusercontent.com/translate_f 412/504
16/12/2021 10:52 Sem título
É importante notar que você não pode usar um QuerySet em cache para construir outros QuerySets,
já que o que você armazenou em cache são, na verdade, os resultados do QuerySet. Então você não pode fazer o
Segue:
[ 439 ]
Página 465
A tag de modelo {% cache%} tem dois argumentos obrigatórios: o tempo limite em segundos
e um nome para o fragmento. Se você precisar armazenar conteúdo em cache, dependendo da dinâmica
dados, você pode fazê-lo passando argumentos adicionais para o {% de cache%} modelo
tag para identificar exclusivamente o fragmento.
{% load cache%}
https://translate.googleusercontent.com/translate_f 413/504
16/12/2021 10:52 Sem título
Você
o armazena
objeto em cache este fragmento de modelo usando o nome module_contents e passando
Módulo atual para ele. Assim, você identifica exclusivamente o fragmento. Isto é
importante para evitar o cache do conteúdo de um módulo e servir o conteúdo errado
quando um módulo diferente é solicitado.
[ 440 ]
Página 466
Capítulo 11
Cache de visualizações
Você pode armazenar em cache a saída de visualizações individuais usando o decorador cache_page
localizado em django.views.decorators.cache . O decorador requer um tempo limite
argumento (em segundos).
Vamos usá-lo em suas visualizações. Edite o arquivo urls.py do aplicativo dos alunos e adicione
a seguinte importação:
O cache por visualização usa o URL para construir a chave do cache. Múltiplo
URLs apontando para a mesma visualização serão armazenados em cache separadamente.
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
[ 441 ]
https://translate.googleusercontent.com/translate_f 414/504
16/12/2021 10:52 Sem título
Página 467
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
# ...
]
CACHE_MIDDLEWARE_ALIAS = 'padrão'
CACHE_MIDDLEWARE_SECONDS = 60 * 15 # 15 minutos
CACHE_MIDDLEWARE_KEY_PREFIX = 'educa'
Nessas configurações, você usa o cache padrão para o middleware de cache e define o
tempo limite de cache global para 15 minutos. Você também especifica um prefixo para todas as chaves de cache para
evite colisões no caso de usar o mesmo back-end do Memcached para vários projetos.
Seu site agora irá armazenar em cache e retornar o conteúdo em cache para todas as solicitações GET .
Você fez isso para testar a funcionalidade de cache por site. No entanto, o por site
o cache não é adequado para você, uma vez que as visualizações de gerenciamento do curso precisam mostrar
dados atualizados para refletir instantaneamente quaisquer alterações. A melhor abordagem a seguir em seu
projeto é armazenar em cache os modelos ou visualizações que são usados para exibir o conteúdo do curso para
alunos.
Você viu uma visão geral dos métodos fornecidos pelo Django para armazenar dados em cache.
Você deve definir sua estratégia de cache com sabedoria e priorizar o mais caro
QuerySets ou cálculos.
Resumo
Neste capítulo, você implementou as visualizações públicas do catálogo de cursos. Você
construiu um sistema para que os alunos se registrassem e se inscrevessem nos cursos. Você também criou o
funcionalidade para renderizar diferentes tipos de conteúdo para os módulos do curso. Finalmente,
você aprendeu a usar a estrutura de cache Django e instalou e
monitorou o back-end do cache do Memcached.
No próximo capítulo, você construirá uma API RESTful para seu projeto usando Django REST
estrutura.
[ 442 ]
https://translate.googleusercontent.com/translate_f 415/504
16/12/2021 10:52 Sem título
Página 468
Neste capítulo, você criará uma API RESTful para sua plataforma de e-learning. Um
API permite que você construa um núcleo comum que pode ser usado em múltiplas plataformas
como sites, aplicativos móveis, plug-ins e assim por diante. Por exemplo, você pode criar
uma API a ser consumida por um aplicativo móvel para sua plataforma de e-learning. Se
você fornece uma API para terceiros, eles serão capazes de consumir informações e
operar com seu aplicativo programaticamente. Uma API permite que os desenvolvedores
automatize ações em sua plataforma e integre seu serviço com outros aplicativos
ou serviços online. Você construirá uma API completa para sua plataforma de e-learning.
[ 443 ]
Página 469
https://translate.googleusercontent.com/translate_f 416/504
16/12/2021 10:52 Sem título
Os formatos mais comuns para troca de dados em APIs RESTful são JSON e XML.
Você construirá uma API RESTful com serialização JSON para seu projeto. Sua API
fornecerá a seguinte funcionalidade:
• Recuperar assuntos
Você pode construir uma API do zero com Django criando visualizações personalizadas. Contudo,
existem vários módulos de terceiros que simplificam a criação de uma API para o seu projeto;
o mais popular entre eles é o framework Django REST.
INSTALLED_APPS = [
# ...
'rest_framework',
]
[ 444 ]
Página 470
Capítulo 12
REST_FRAMEWORK = {
https://translate.googleusercontent.com/translate_f 417/504
16/12/2021 10:52 Sem título
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.jangoModelPermissionsOrAnonReadOnly'
]
}
Você pode fornecer uma configuração específica para sua API usando o REST_
Configuração FRAMEWORK . A estrutura REST oferece uma ampla gama de configurações para configurar
comportamentos padrão. A configuração DEFAULT_PERMISSION_CLASSES especifica
as permissões padrão para ler, criar, atualizar ou excluir objetos. Você define
DjangoModelPermissionsOrAnonReadOnly como a única classe de permissão padrão.
Esta classe depende do sistema de permissões do Django para permitir que os usuários criem, atualizem,
ou excluir objetos, enquanto fornece acesso somente leitura para usuários anônimos. Você irá
aprenda mais sobre as permissões posteriormente na seção Adicionando permissões às visualizações .
Para obter uma lista completa das configurações disponíveis para a estrutura REST, você pode visitar https: //
www.django-rest-framework.org/api-guide/settings/.
Definindo serializadores
Depois de configurar a estrutura REST, você precisa especificar como seus dados serão
serializado. Os dados de saída devem ser serializados em um formato específico e os dados de entrada
será desserializado para processamento. A estrutura fornece as seguintes classes
para construir serializadores para objetos únicos:
Vamos construir seu primeiro serializador. Crie a seguinte estrutura de arquivos dentro dos cursos
diretório do aplicativo:
api /
__init__.py
serializers.py
Você construirá todas as funcionalidades da API dentro do diretório api para manter tudo
bem organizado. Edite o arquivo serializers.py e adicione o seguinte código:
[ 445 ]
Página 471
Este é o serializador para o modelo Assunto . Os serializadores são definidos de forma semelhante
moda para as classes Form e ModelForm do Django . A classe Meta permite que você especifique
o modelo a ser serializado e os campos a serem incluídos para serialização. Todos os campos do modelo
será incluído se você não definir um atributo de campos .
https://translate.googleusercontent.com/translate_f 418/504
16/12/2021 10:52 Sem título
Vamos experimentar o seu serializador. Abra a linha de comando e inicie o shell do Django com o
seguinte comando:
Neste exemplo, você obtém um objeto Subject , cria uma instância de SubjectSerializer ,
e acessar os dados serializados. Você pode ver que os dados do modelo são convertidos em
Tipos de dados nativos do Python.
Vamos ver como analisar os dados recebidos. Execute o seguinte código no shell Python:
[ 446 ]
Página 472
Capítulo 12
Dada uma entrada de string JSON, você pode usar a classe JSONParser fornecida por REST
framework para convertê-lo em um objeto Python.
O framework REST também inclui classes Renderer que permitem a você formatar API
respostas. A estrutura determina qual renderizador usar por meio do conteúdo
negociação inspecionando o cabeçalho de aceitação da solicitação para determinar o
tipo de conteúdo para a resposta. Opcionalmente, o renderizador é determinado pelo formato
sufixo do URL. Por exemplo, o URL http://127.0.0.1:8000/api/data.json
pode ser um endpoint que aciona o JSONRenderer para retornar um JSON
resposta.
Volte para o shell e execute o seguinte código para renderizar o objeto serializador
do exemplo anterior do serializador:
https://translate.googleusercontent.com/translate_f 419/504
16/12/2021 10:52 Sem título
Você verá a seguinte saída:
b '{"id": 4, "title": "Programming", "slug": "programming"}'
Você pode encontrar mais informações sobre renderizadores e analisadores em https: // www.
django-rest-framework.org/api-guide/renderers/ e https: //www.django-
rest-framework.org/api-guide/parsers/ , respectivamente.
Vamos criar visualizações de lista e detalhes para recuperar objetos Subject . Crie um novo arquivo dentro
o diretório cursos / api / e nomeie-o views.py . Adicione o seguinte código a ele:
[ 447 ]
Página 473
Vamos adicionar padrões de URL para suas visualizações. Crie um novo arquivo dentro dos cursos / api /
diretório, chame -o de urls.py e faça com que tenha a seguinte aparência:
app_name = 'cursos'
urlpatterns = [
caminho ('assuntos /',
https://translate.googleusercontent.com/translate_f 420/504
16/12/2021 10:52 Sem título
views.SubjectListView.as_view (),
name = 'subject_list'),
Edite o arquivo urls.py principal do projeto educa e inclua os padrões da API, como
segue:
urlpatterns = [
# ...
caminho ('api /', incluir ('cursos.api.urls', namespace = 'api')),
]
Você usa o namespace api para seus URLs de API. Certifique-se de que seu servidor está funcionando
com o comando python manage.py runserver . Abra o shell e recupere o
URL http://127.0.0.1:8000/api/subjects/ com curl , da seguinte forma:
curl http://127.0.0.1:8000/api/subjects/
[ 448 ]
Página 474
Capítulo 12
{
"id": 1,
"título": "Matemática",
"lesma": "matemática"
},
{
"id": 2,
"título": "Música",
"slug": "música"
},
{
"id": 3,
"título": "Física",
"lesma": "física"
},
"id": 4,
"título": "Programação",
"slug": "programação"
}
]
Para obter uma resposta JSON mais legível e bem recuada, você pode usar curl com
o utilitário json_pp , da seguinte maneira:
https://translate.googleusercontent.com/translate_f 421/504
16/12/2021 10:52 Sem título
A resposta HTTP contém uma lista de objetos Subject no formato JSON. Se seu
sistema operacional não vem com o curl instalado, você pode baixá-lo em
https://curl.haxx.se/dlwiz/. Em vez de curl , você também pode usar qualquer outra ferramenta
para enviar solicitações HTTP personalizadas, incluindo uma extensão de navegador como Postman,
onde você pode pegar https://www.getpostman.com/.
[ 449 ]
Página 475
https://translate.googleusercontent.com/translate_f 422/504
16/12/2021 10:52 Sem título
[ 450 ]
Página 476
Capítulo 12
Vamos dar uma olhada em como um objeto Course é serializado. Abra o shell, execute python
shell manage.py e execute o seguinte código:
Você deseja incluir mais informações sobre cada módulo, então você precisa serializar
Objetos de módulo e aninhá-los. Modifique o código anterior da api / serializadores.
arquivo py do aplicativo de cursos para torná-lo o seguinte:
classe Meta:
modelo = curso
campos = ['id', 'assunto', 'título', 'slug', 'visão geral',
'criado', 'proprietário', 'módulos']
Você define ModuleSerializer para fornecer serialização para o modelo de módulo . Então,
você adiciona um atributo de módulos a CourseSerializer para aninhar o ModuleSerializer
serializador. Você define many = True para indicar que está serializando vários objetos.
O parâmetro read_only indica que este campo é somente leitura e não deve ser
incluído em qualquer entrada para criar ou atualizar objetos.
[ 451 ]
https://translate.googleusercontent.com/translate_f 423/504
16/12/2021 10:52 Sem título
Página 477
"módulos": [
{
"pedido": 0,
"title": "Introdução à visão geral",
"descrição": "Uma breve visão geral sobre o Web Framework."
},
{
"pedido": 1,
"title": "Configurando Django",
"descrição": "Como instalar o Django."
},
...
]
Você vai criar uma visão para os usuários se inscreverem nos cursos. Edite o api / views.py
arquivo do aplicativo de cursos e adicione o seguinte código a ele:
[ 452 ]
https://translate.googleusercontent.com/translate_f 424/504
16/12/2021 10:52 Sem título
Página 478
Capítulo 12
Teoricamente, agora você pode executar uma solicitação POST para inscrever o usuário atual
em um curso. No entanto, você precisa ser capaz de identificar o usuário e prevenir
usuários não autenticados acessem esta visão. Vamos ver como a autenticação de API
e as permissões funcionam.
Autenticação de tratamento
A estrutura REST fornece classes de autenticação para identificar o desempenho do usuário
o pedido. Se a autenticação for bem-sucedida, a estrutura define o autenticado
Objeto de usuário em request.user . Se nenhum usuário estiver autenticado, uma instância do Django
AnonymousUser é definido em vez disso.
[ 453 ]
Página 479
https://translate.googleusercontent.com/translate_f 425/504
16/12/2021 10:52 Sem título
[ 454 ]
Página 480
Capítulo 12
https://translate.googleusercontent.com/translate_f 426/504
16/12/2021 10:52 Sem título
CABEÇA ou OPÇÕES .
• DjangoModelPermissions : Permissões vinculadas a django.contrib.auth . O
view requer um atributo queryset . Apenas usuários autenticados com modelo
as permissões atribuídas recebem permissão.
• DjangoObjectPermissions : Permissões do Django em uma base por objeto.
Se os usuários tiverem permissão negada, eles geralmente obterão um dos seguintes HTTP
códigos de erro:
Certifique-se de que o servidor de desenvolvimento esteja em execução. Abra o shell e execute o seguinte
comando:
[ 455 ]
Página 481
Você obteve um código HTTP 401 conforme o esperado, pois você não está autenticado. Vamos usar
autenticação básica com um de seus usuários. Execute o seguinte comando, substituindo
aluno: senha com as credenciais de um usuário existente:
https://translate.googleusercontent.com/translate_f 427/504
16/12/2021 10:52 Sem título
...
{"inscrito": verdadeiro}
Você pode acessar o site de administração e verificar se o usuário agora está inscrito no
o curso.
Vamos criar um conjunto de visualizações para o modelo de curso . Edite o arquivo api / views.py e adicione o
seguinte código para ele:
Edite o arquivo api / urls.py e crie um roteador para o seu conjunto de visualizações, da seguinte maneira:
[ 456 ]
Página 482
Capítulo 12
router = routers.DefaultRouter ()
router.register ('cursos', visualizações.CourseViewSet)
urlpatterns = [
# ...
caminho ('', incluir (roteador.urls)),
]
Você cria um objeto DefaultRouter e registra seu conjunto de visualizações com os cursos
prefixo. O roteador se encarrega de gerar URLs automaticamente para seu conjunto de visualizações.
https://translate.googleusercontent.com/translate_f 428/504
16/12/2021 10:52 Sem título
[ 457 ]
Página 483
https://translate.googleusercontent.com/translate_f 429/504
16/12/2021 10:52 Sem título
4. Você adiciona
retornar umao resposta
usuário atual ao relacionamento
de sucesso muitos para muitos dos alunos e
personalizada.
Edite o arquivo api / urls.py e remova o seguinte URL, já que você não precisa dele
não mais:
O URL para se inscrever nos cursos agora é gerado automaticamente pelo roteador. O
A URL permanece a mesma, já que é construída dinamicamente usando seu nome de ação registrar .
[ 458 ]
Página 484
Capítulo 12
Esses métodos devem retornar True para conceder acesso ou False caso contrário.
https://translate.googleusercontent.com/translate_f 430/504
16/12/2021 10:52 Sem título
def to_representation (self, value):
return value.render ()
classe Meta:
model = Content
campos = ['pedido', 'item']
Neste código, você define um campo personalizado criando uma subclasse do serializador RelatedField
campo fornecido pela estrutura REST e substituindo o to_representation ()
método. Você define o serializador ContentSerializer para o modelo de conteúdo e
use o campo personalizado para a chave estrangeira genérica do item .
[ 459 ]
Página 485
Você precisa de um serializador alternativo para o modelo do Módulo que inclui seu conteúdo,
e um serializador de curso estendido também. Edite o arquivo api / serializers.py e
adicione o seguinte código a ele:
classe Meta:
modelo = Módulo
campos = ['pedido', 'título', 'descrição', 'conteúdo']
classe Meta:
modelo = curso
campos = ['id', 'assunto', 'título', 'slug',
'visão geral', 'criado', 'proprietário', 'módulos']
Vamos criar uma visão que imita o comportamento da ação retrieve () , mas inclui
o conteúdo do curso. Edite o arquivo api / views.py e adicione o seguinte método ao
Classe CourseViewSet :
• Você usa o decorador de ação com o parâmetro detail = True para especificar um
ação que é executada em um único objeto.
https://translate.googleusercontent.com/translate_f 431/504
16/12/2021 10:52 Sem título
• Você especifica que apenas o método GET é permitido para esta ação.
• Você usa a nova classe do serializador CourseWithContentsSerializer que
inclui o conteúdo do curso renderizado.
[ 460 ]
Página 486
Capítulo 12
{
"pedido": 0,
"title": "Introdução ao Django",
"descrição": "Breve introdução ao Django Web Framework.",
"conteúdo": [
{
"pedido": 0,
"item": "<p> Conheça o Django. Django é um
Estrutura da Web Python
... </p> "
},
{
"pedido": 1,
"item": "\ n <iframe width = \" 480 \ "height = \" 360 \ "
src = \ "http://www.youtube.com/embed/bgV39DlmZ2U?
wmode = opaco \ "
frameborder = \ "0 \" allowfullscreen> </iframe> \ n "
}
]
}
Você construiu uma API simples que permite que outros serviços acessem o curso
aplicativo de forma programática. A estrutura REST também permite que você lide com a criação
e edição de objetos com o conjunto de visualizações ModelViewSet. Nós cobrimos o principal
aspectos da estrutura Django REST, mas você encontrará mais informações sobre
seus recursos em sua extensa documentação em https: //www.django-rest-
framework.org/.
[ 461 ]
https://translate.googleusercontent.com/translate_f 432/504
16/12/2021 10:52 Sem título
Página 487
Você vai criar um aplicativo Python simples que usa a API RESTful para
recupere todos os cursos disponíveis e, em seguida, inscreva um aluno em todos eles. Você vai aprender
como autenticar na API usando autenticação básica HTTP e realizar
Solicitações GET e POST .
api_examples /
matricula_all.py
educa /
...
pedidos de importação
base_url = 'http://127.0.0.1:8000/api/'
[ 462 ]
https://translate.googleusercontent.com/translate_f 433/504
16/12/2021 10:52 Sem título
Página 488
Capítulo 12
3. Você usa o método json () do objeto de resposta para decodificar os dados JSON
retornado pela API.
4. Você imprime o atributo do título de cada curso.
python matricula_all.py
Você verá uma saída com uma lista de todos os títulos dos cursos, como esta:
pedidos de importação
username = ''
senha = ''
base_url = 'http://127.0.0.1:8000/api/'
Substitua os valores das variáveis de nome de usuário e senha pelas credenciais de
um usuário existente.
[ 463 ]
Página 489
https://translate.googleusercontent.com/translate_f 434/504
16/12/2021 10:52 Sem título
Você pode usar diferentes tipos de autenticação com solicitações. Você pode encontrar mais
informações sobre autenticação com solicitações em https: //requests.readthedocs.
io / en / master / user / authentication / .
python matricula_all.py
Excelente! Você inscreveu o usuário com sucesso em todos os cursos disponíveis usando a API.
Você verá uma mensagem de inscrito com sucesso para cada curso na plataforma. Como
como você pode ver, é muito fácil consumir a API de qualquer outro aplicativo. Você pode
construir sem esforço outras funcionalidades com base na API e permitir que outros integrem seu
API em seus aplicativos.
[ 464 ]
Página 490
Capítulo 12
Resumo
Neste capítulo, você aprendeu como usar o framework Django REST para construir um RESTful
https://translate.googleusercontent.com/translate_f 435/504
16/12/2021 10:52 Sem título
API para seu projeto. Você criou serializadores e visualizações para modelos e construiu
visualizações de API personalizadas. Você também adicionou autenticação à sua API e restringiu
acesso às visualizações da API usando permissões. Em seguida, você descobriu como criar
permissões e você implementou conjuntos de visualizações e roteadores. Finalmente, você usou o
Biblioteca de solicitações para consumir a API de um script Python externo.
O próximo capítulo irá ensiná-lo a construir um servidor de chat usando canais Django.
Você implementará a comunicação assíncrona usando WebSockets e
use o Redis para configurar uma camada de canal.
[ 465 ]
Página 492
491
https://translate.googleusercontent.com/translate_f 436/504
16/12/2021 10:52 Sem título
[ 467 ]
Página 493
Usando canais, você pode implementar facilmente funcionalidades em tempo real ou assíncronas
em seu projeto, além de suas visualizações síncronas HTTP padrão. Você irá
comece adicionando um novo aplicativo ao seu projeto. O novo aplicativo conterá
a lógica para o servidor de bate-papo.
Execute o seguinte comando a partir do diretório educa do projeto para criar o novo
estrutura do arquivo do aplicativo:
INSTALLED_APPS = [
# ...
'bate-papo',
]
https://translate.googleusercontent.com/translate_f 437/504
16/12/2021 10:52 Sem título
O novo aplicativo de bate-papo agora está ativo em seu projeto.
Edite o arquivo views.py do novo aplicativo de bate-papo e adicione o seguinte código a ele:
@login_required
def course_chat_room (solicitação, course_id):
experimentar:
# recuperar o curso com o id fornecido pelo usuário atual
curso = request.user.courses_joined.get (id = course_id)
exceto:
# usuário não é aluno do curso ou curso não existe
return HttpResponseForbidden ()
return render (request, 'chat / room.html', {'course': course})
[ 468 ]
Página 494
Capítulo 13
Você acessa os cursos nos quais o usuário está inscrito através do relacionamento cursos_
juntou - se e você recupera o curso com o id fornecido desse subconjunto de cursos.
Se o curso com o id fornecido não existir ou o usuário não estiver inscrito nele, você
retornar uma resposta HttpResponseForbidden , que se traduz em uma resposta HTTP
com status 403 . Se o curso com o id fornecido existe e o usuário está inscrito em
, você renderiza o template chat / room.html , passando o objeto de curso para o
contexto do modelo.
Você precisa adicionar um padrão de URL para esta visualização. Crie um novo arquivo dentro do chat
diretório do aplicativo e nomeá-la urls.py . Adicione o seguinte código a ele:
app_name = 'chat'
urlpatterns = [
path ('room / <int: course_id> /', views.course_chat_room,
name = 'course_chat_room'),
]
Este é o arquivo de padrões de URL inicial para o aplicativo de bate - papo . Você define o curso_
padrão de URL chat_room , incluindo o parâmetro course_id com o prefixo int ,
como você espera apenas um valor inteiro aqui.
https://translate.googleusercontent.com/translate_f 438/504
16/12/2021 10:52 Sem título
Incluir os novos padrões de URL do aplicativo de bate - papo nos principais padrões de URL de
o projeto. Edite o arquivo urls.py principal do projeto educa e adicione o seguinte
linha para ele:
urlpatterns = [
# ...
caminho ('chat /', include ('chat.urls', namespace = 'chat')),
]
Os padrões de URL para o aplicativo de bate - papo são adicionados ao projeto no bate - papo / caminho.
Você precisa criar um modelo para a visão course_chat_room . Este modelo irá
contém uma área para visualizar as mensagens que são trocadas no chat e um texto
entrada com um botão de envio para enviar mensagens de texto para o chat.
modelos/
bate-papo/
room.html
[ 469 ]
Página 495
{% extends "base.html"%}
{% block content%}
<div id = "chat">
</div>
<div id = "chat-input">
<input id = "chat-message-input" type = "text">
<input id = "chat-message-submit" type = "submit" value = "Send">
</div>
{% endblock%}
{% block domready%}
{% endblock%}
Este é o modelo para a sala de chat do curso. Neste modelo, você estende a base.
template html do seu projeto e preencha seu bloco de conteúdo . No modelo, você define
um elemento HTML <div> com o ID do bate - papo que você usará para exibir o bate-papo
mensagens enviadas pelo usuário e por outros alunos. Você também define um segundo <div>
elemento com uma entrada de texto e um botão de envio que permitirá ao usuário enviar
mensagens. Você inclui o bloco domready definido pelo modelo base.html ,
que você vai implementar posteriormente usando JavaScript, para estabelecer uma conexão
com um WebSocket e enviar ou receber mensagens.
https://translate.googleusercontent.com/translate_f 439/504
16/12/2021 10:52 Sem título
Esta é a tela da sala de bate-papo do curso que os alunos usarão para discutir tópicos dentro de um
curso.
[ 470 ]
Página 496
Capítulo 13
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
# 'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.cache.FetchFromCacheMiddleware',
# ...
]
Você desativou o cache por site em seu projeto para evitar a nova sala de chat
vista de ser armazenada em cache. A seguir, você aprenderá como adicionar canais ao seu Django
projeto para implementar um servidor de bate-papo em tempo real.
https://translate.googleusercontent.com/translate_f 440/504
16/12/2021 10:52 Sem título
para trabalhar com aplicativos assíncronos, você precisa usar outra interface chamada
ASGI, que também pode lidar com solicitações WebSocket. ASGI é o Python emergente
padrão para servidores e aplicativos da web assíncronos.
[ 471 ]
Página 497
Django 3 vem com suporte para execução de Python assíncrona por meio de ASGI, mas
ele ainda não oferece suporte a visualizações assíncronas ou middleware. No entanto, como mencionado,
Os canais estendem o Django para lidar não apenas com HTTP, mas também com protocolos que requerem
conexões de longa duração, como WebSockets e chatbots.
Você pode encontrar mais informações sobre como implantar Django com ASGI em https: //
docs.djangoproject.com/en/3.0/howto/deployment/asgi/.
Quando uma solicitação HTTP é enviada pelo navegador para o servidor web, Django lida com
a solicitação e passa o objeto HttpRequest para a visualização correspondente. O
view processa a solicitação e retorna um objeto HttpResponse que é enviado de volta
para o navegador como uma resposta HTTP. Não há nenhum mecanismo para manter um aberto
conexão ou enviar dados para o navegador sem uma solicitação HTTP associada.
[ 472 ]
https://translate.googleusercontent.com/translate_f 441/504
16/12/2021 10:52 Sem título
Página 498
Capítulo 13
Os canais substituem o ciclo de solicitação / resposta do Django por mensagens que são enviadas
em todos os canais. As solicitações HTTP ainda são roteadas para visualizar funções usando Django,
mas eles são roteados por canais. Isso permite o manuseio de mensagens WebSockets
também, onde você tem produtores e consumidores que trocam mensagens entre
uma camada de canal. Canais preserva a arquitetura síncrona do Django, permitindo
você pode escolher entre escrever código síncrono e código assíncrono, ou um
combinação de ambos.
Instalando Canais
Você irá adicionar canais ao seu projeto e configurar o ASGI básico necessário
roteamento de aplicativos para que ele gerencie solicitações HTTP.
[ 473 ]
https://translate.googleusercontent.com/translate_f 442/504
16/12/2021 10:52 Sem título
Página 499
INSTALLED_APPS = [
# ...
'canais',
]
Channels espera que você defina um único aplicativo raiz que será executado por
todos os pedidos. Você pode definir o aplicativo raiz adicionando o ASGI_APPLICATION
configuração para o seu projeto. Isso é semelhante à configuração ROOT_URLCONF que aponta para o
padrões básicos de URL do seu projeto. Você pode colocar o aplicativo raiz em qualquer lugar em
seu projeto, mas é recomendado colocá-lo em um arquivo de nível de projeto denominado routing.
py .
Crie um novo arquivo dentro do diretório do projeto educa próximo ao arquivo settings.py
e nomeie-o routing.py .
application = ProtocolTypeRouter ({
# vazio por enquanto
})
ASGI_APPLICATION = 'educa.routing.application'
No código anterior, você define o aplicativo ASGI principal que será executado
ao servir seu projeto Django por meio de ASGI. Você usa o ProtocolTypeRouter
classe fornecida por canais como o principal ponto de entrada de seu sistema de roteamento.
ProtocolTypeRouter usa um dicionário que mapeia tipos de comunicação como
http ou websocket para aplicativos ASGI. Você instancia esta classe com um vazio
dicionário que mais tarde você preencherá com uma rota para seu aplicativo de chat WebSocket
consumidor.
Quando canais são adicionados à configuração INSTALLED_APPS , ele assume o controle sobre o
comando runserver , substituindo o servidor de desenvolvimento Django padrão. além do mais
lidar com o roteamento de URL para visualizações do Django para solicitações síncronas, os canais
o servidor de desenvolvimento também gerencia rotas para consumidores WebSocket.
[ 474 ]
Página 500
https://translate.googleusercontent.com/translate_f 443/504
16/12/2021 10:52 Sem título
Capítulo 13
Agora que o Channels está instalado em seu projeto, você pode construir o servidor de bate-papo para
cursos. Para implementar o servidor de bate-papo para o seu projeto, você precisará
execute os seguintes passos:
1. Configure um consumidor : os consumidores são pedaços individuais de código que podem lidar
WebSockets de uma maneira muito semelhante às visualizações HTTP tradicionais. Você vai construir
um consumidor para ler e escrever mensagens em um canal de comunicação.
2. Configure o roteamento : os canais fornecem classes de roteamento que permitem que você
combine e empilhe seus consumidores. Você vai configurar o roteamento de URL para
seu consumidor de bate-papo.
4. Habilite uma camada de canal : as camadas de canal permitem que você converse entre diferentes
instâncias de um aplicativo. Eles são uma parte útil de fazer uma distribuição
aplicação em tempo real. Você configurará uma camada de canal usando o Redis.
[ 475 ]
Página 501
Escrevendo um consumidor
Os consumidores são equivalentes às visualizações do Django para aplicativos assíncronos. Como
https://translate.googleusercontent.com/translate_f 444/504
16/12/2021 10:52 Sem título
mencionados, eles lidam com WebSockets de uma maneira muito semelhante a como as visualizações tradicionais
lidar com solicitações HTTP. Os consumidores são aplicativos ASGI que podem lidar com mensagens,
notificações e outras coisas. Ao contrário das visualizações do Django, os consumidores são construídos para
comunicação em execução. URLs são mapeados para consumidores por meio de classes de roteamento
que permitem combinar e empilhar consumidores.
Vamos implementar um consumidor básico que seja capaz de aceitar conexões WebSocket
e ecoa todas as mensagens que recebe do WebSocket de volta para ele. Esta inicial
funcionalidade permitirá que o aluno envie mensagens ao consumidor e receba
de volta as mensagens que envia.
Crie um novo arquivo dentro do diretório do aplicativo de bate - papo e nomeie-o consumidor.py .
Adicione o seguinte código a ele:
importar json
from channels.generic.websocket import WebsocketConsumer
• connnect () : Chamado quando uma nova conexão é recebida. Você aceita qualquer
conexão com self.accept () . Você também pode rejeitar uma conexão
chamando self.close () .
• desconectar () : Chamado quando o soquete fecha. Você usa passe porque você
não precisa implementar nenhuma ação quando um cliente fecha a conexão.
[ 476 ]
Página 502
Capítulo 13
• receber () : Chamado sempre que dados são recebidos. Você espera que o texto seja recebido
como text_data (também pode ser binary_data para dados binários). Você trata
os dados de texto recebidos como JSON. Portanto, você usa json.loads () para carregar
os dados JSON recebidos em um dicionário Python. Você acessa a mensagem
chave, que você espera que esteja presente na estrutura JSON recebida. Para
ecoando a mensagem, você a envia de volta para o WebSocket consigo mesmo.
send () , transformando-o novamente no formato JSON através de json.dumps () .
Encaminhamento
Você precisa definir uma URL para rotear conexões para o consumidor ChatConsumer que você
tenham implementado. Canais oferece classes de roteamento que permitem combinar
e empilhar consumidores para despachar com base na conexão. Você pode pensar
deles como o sistema de roteamento de URL do Django para aplicativos assíncronos.
Crie um novo arquivo dentro do diretório do aplicativo de bate - papo e nomeie-o como routing.py .
Adicione o seguinte código a ele:
websocket_urlpatterns = [
re_path (r'ws / chat / room / (? P <course_id> \ d +) / $ ', consumidores.
ChatConsumer),
]
Neste código, você mapeia um padrão de URL com a classe ChatConsumer que você definiu
no arquivo chat / consumidores.py . Você usa o re_path do Django para definir o caminho com
expressões regulares. O URL inclui um parâmetro inteiro chamado course_id .
Este parâmetro estará disponível no escopo do consumidor e permitirá que você
para identificar a sala de chat do curso à qual o usuário está se conectando.
[ 477 ]
Página 503
Edite o arquivo global routing.py localizado próximo ao arquivo settings.py para que pareça
como isso:
application = ProtocolTypeRouter ({
'websocket': AuthMiddlewareStack (
URLRouter (
chat.routing.websocket_urlpatterns
)
),
})
Neste código, você usa URLRouter para mapear conexões de websocket para os padrões de URL
definido na lista websocket_urlpatterns do arquivo de roteamento do aplicativo de bate - papo .
O roteador ProtocolTypeRouter padrão mapeia automaticamente as solicitações HTTP para
https://translate.googleusercontent.com/translate_f 446/504
16/12/2021 10:52 Sem título
as visualizações padrão do Django se nenhum mapeamento de http específico for fornecido. Você também usa
AuthMiddlewareStack . A classe AuthMiddlewareStack fornecida por Channels
suporta autenticação Django padrão, onde os detalhes do usuário são armazenados em
a sessão. Você planeja acessar a instância do usuário no escopo do consumidor para
identificar o usuário que envia uma mensagem.
{% block domready%}
var url = 'ws: //' + window.location.host +
'/ ws / chat / room /' + '{{course.id}} /';
var chatSocket = novo WebSocket (url);
{% endblock%}
[ 478 ]
Página 504
Capítulo 13
Você define um URL com o protocolo WebSocket, que se parece com ws: // (ou wss: //
para WebSockets seguros, assim como https: // ). Você constrói o URL usando o atual
localização do navegador, obtida em window.location.host . O resto
do URL é construído com o caminho para o padrão de URL da sala de chat que você definiu
no arquivo routing.py do aplicativo de bate - papo .
Você escreve a URL inteira em vez de construí-la por meio de seu nome, porque Channels
não fornecem uma maneira de reverter URLs. Você usa o ID do curso atual para gerar o
URL para o curso atual e armazene o URL em uma nova variável chamada url .
Você então abre uma conexão WebSocket para a URL armazenada usando novo
WebSocket (url) . Você atribui o objeto cliente WebSocket instanciado ao novo
variável chatSocket .
https://translate.googleusercontent.com/translate_f 447/504
16/12/2021 10:52 Sem título
Se você usar as ferramentas de desenvolvedor de navegador para rastrear conexões de rede, também pode
consulte as informações para a conexão WebSocket que foi estabelecida.
[ 479 ]
Página 505
Figura 13.4: As ferramentas de desenvolvedor do navegador mostrando que a conexão WebSocket foi estabelecida
Agora que você pode se conectar ao WebSocket, é hora de interagir com ele.
Você implementará os métodos para lidar com eventos comuns, como receber um
mensagem e fechando a conexão. Edite o modelo chat / room.html do
aplicativo de bate-papo e modifique o bloco domready , da seguinte forma:
{% block domready%}
var url = 'ws: //' + window.location.host +
'/ ws / chat / room /' + '{{course.id}} /';
var chatSocket = novo WebSocket (url);
https://translate.googleusercontent.com/translate_f 448/504
16/12/2021 10:52 Sem título
• onmessage : Disparado quando os dados são recebidos por meio do WebSocket. Você analisa
a mensagem, que você espera no formato JSON, e acesse sua mensagem
atributo. Em seguida, você anexa um novo elemento <div> com a mensagem ao
Elemento HTML com o ID do bate - papo . Isso adicionará novas mensagens ao registro do bate-papo,
enquanto mantém todas as mensagens anteriores que foram adicionadas ao log. Você
role o log de bate-papo <div> até o final para garantir que a nova mensagem seja
visibilidade. Você consegue isso rolando até a altura total de rolagem do
log de bate-papo, que pode ser obtido acessando seu atributo srollHeight .
• onclose : Disparado quando a conexão com o WebSocket é fechada. Você não
espere fechar a conexão e, portanto, você escreve o erro Chat socket
fechado inesperadamente no log do console se isso acontecer.
[ 480 ]
Página 506
Capítulo 13
Você implementou a ação para exibir a mensagem quando uma nova mensagem é
recebido. Você precisa implementar a funcionalidade para enviar mensagens para o soquete
também.
$ submit.click (function () {
var mensagem = $ input.val ();
if (mensagem) {
// enviar mensagem no formato JSON
chatSocket.send (JSON.stringify ({'mensagem': mensagem}));
// limpar a entrada
$ input.val ('');
// retornar o foco
$ input.focus ();
}
});
Neste código, você define uma função para o evento de clique do botão de envio, que
você seleciona com o ID chat-message-submit . Quando o botão é clicado, você
execute as seguintes ações:
1. Você leu a mensagem inserida pelo usuário a partir do valor da entrada de texto
elemento com o ID chat-message-input
2. Você verifica se a mensagem tem algum conteúdo com if (mensagem)
3. Se o usuário inseriu uma mensagem, você forma o conteúdo JSON, como
{'mensagem': 'string inserida pelo usuário'} usando JSON.
stringify ()
4. Você envia o conteúdo JSON por meio do WebSocket, chamando o método send ()
método do cliente chatSocket
5. Você limpa o conteúdo da entrada de texto definindo seu valor como um vazio
string com $ input.val ('')
https://translate.googleusercontent.com/translate_f 449/504
16/12/2021 10:52 Sem título
6. Você retorna o foco para a entrada de texto com $ input.focus () para que o usuário
pode escrever uma nova mensagem imediatamente
[ 481 ]
Página 507
A fim de melhorar a experiência do usuário, você dará foco à entrada de texto como
assim que a página for carregada para que o usuário possa digitar diretamente nela. Você também irá capturar
eventos pressionados de tecla do teclado para identificar a tecla Enter / Return e disparar o clique
evento no botão enviar. O usuário poderá clicar no botão ou pressionar
a tecla Enter / Return para enviar uma mensagem.
$ input.focus ();
$ input.keyup (function (e) {
if (e.which === 13) {
// envie com a tecla enter / return
$ submit.click ();
}
});
Neste código, você dá o foco para a entrada de texto. Você também define uma função para o
evento keyup () da entrada. Para qualquer tecla que o usuário pressiona, você verifica se é
o código-chave é 13 . Este é o código-chave que corresponde à tecla Enter / Return . Você
pode usar o recurso https://keycode.info para identificar o código-chave de qualquer chave.
Se a tecla Enter / Return for pressionada, você dispara o evento de clique no botão de envio
para enviar a mensagem ao WebSocket.
{% block domready%}
var url = 'ws: //' + window.location.host +
'/ ws / chat / room /' + '{{course.id}} /';
var chatSocket = novo WebSocket (url);
[ 482 ]
https://translate.googleusercontent.com/translate_f 450/504
16/12/2021 10:52 Sem título
Página 508
Capítulo 13
$ submit.click (function () {
var mensagem = $ input.val ();
if (mensagem) {
// enviar mensagem no formato JSON
chatSocket.send (JSON.stringify ({'mensagem': mensagem}));
// limpar a entrada
$ input.val ('');
// retornar o foco
$ input.focus ();
}
});
$ input.focus ();
$ input.keyup (function (e) {
if (e.which === 13) {
// envie com a tecla enter / return
$ submit.click ();
}
});
{% endblock%}
Figura 13.5: A página da sala de chat, incluindo mensagens enviadas através do WebSocket
[ 483 ]
https://translate.googleusercontent.com/translate_f 451/504
16/12/2021 10:52 Sem título
Página 509
Canais e grupos
As camadas de canal fornecem duas abstrações para gerenciar as comunicações: canais
e grupos:
• Canal : Você pode pensar em um canal como uma caixa de entrada onde as mensagens podem ser
enviado para ou como uma fila de tarefas. Cada canal tem um nome. Mensagens são enviadas
a um canal por qualquer pessoa que saiba o nome do canal e, em seguida, dado a
consumidores ouvindo nesse canal.
• Grupo : vários canais podem ser agrupados em um grupo. Cada grupo tem
um nome. Um canal pode ser adicionado ou removido de um grupo por qualquer pessoa
quem sabe o nome do grupo. Usando o nome do grupo, você também pode enviar
uma mensagem para todos os canais do grupo.
Você trabalhará com grupos de canais para implementar o servidor de bate-papo. Ao criar um
grupo de canais para cada sala de chat do curso, as instâncias do ChatConsumer serão
capazes de se comunicarem uns com os outros.
[ 484 ]
Página 510
https://translate.googleusercontent.com/translate_f 452/504
16/12/2021 10:52 Sem título
Capítulo 13
Se você ainda não instalou o Redis, pode encontrar as instruções de instalação no Capítulo 6 ,
Rastreando as ações do usuário .
Para usar o Redis como uma camada de canal, você deve instalar o channels-redis
pacote. Instale o channels-redis em seu ambiente virtual com o seguinte
comando:
CHANNEL_LAYERS = {
'padrão': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [('127.0.0.1', 6379)],
},
},
}
Vamos tentar a camada de canal. Inicialize o servidor Redis usando o seguinte comando
do shell em seu diretório Redis:
src / redis-server
Para verificar se a camada do canal pode se comunicar com o Redis, escreva o seguinte
código para enviar uma mensagem a um canal de teste denominado test_channel e recebê-la de volta:
{'mensagem': 'olá'}
[ 485 ]
Página 511
No código anterior, você envia uma mensagem a um canal de teste por meio da camada de canal,
e então você o recupera da camada do canal. A camada do canal está se comunicando
https://translate.googleusercontent.com/translate_f 453/504
16/12/2021 10:52 Sem título
com sucesso com Redis.
importar json
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
# ...
Neste código, você importa a função auxiliar async_to_sync () para encapsular chamadas
para métodos de camada de canal assíncronos. ChatConsumer é um síncrono
Consumidor do WebsocketConsumer , mas precisa chamar métodos assíncronos de
a camada do canal.
[ 486 ]
Página 512
Capítulo 13
https://translate.googleusercontent.com/translate_f 454/504
16/12/2021 10:52 Sem título
4. Você mantém a chamada self.accept () para aceitar a conexão WebSocket.
Quando o consumidor ChatConsumer recebe uma nova conexão WebSocket, ele adiciona
o canal para o grupo associado ao curso em seu escopo. O consumidor é
agora é capaz de receber qualquer mensagem enviada ao grupo.
# ...
[ 487 ]
Página 513
• tipo : o tipo de evento. Esta é uma chave especial que corresponde ao nome de
o método que deve ser chamado nos consumidores que recebem o evento. Você
https://translate.googleusercontent.com/translate_f 455/504
16/12/2021 10:52 Sem título
pode implementar um método no consumidor com o mesmo nome da mensagem
digite para que seja executado toda vez que uma mensagem com esse tipo específico for
recebido.
• mensagem : a mensagem real que você está enviando.
Você nomeia este método chat_message () para coincidir com a chave de tipo que é enviada para o
grupo de canais quando uma mensagem é recebida do WebSocket. Quando uma mensagem
com o tipo chat_message é enviado ao grupo, todos os consumidores inscritos no grupo
receberá a mensagem e executará o método chat_message () . No chat_
método message () , você envia a mensagem de evento recebida para o WebSocket.
importar json
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
[ 488 ]
Página 514
Capítulo 13
https://translate.googleusercontent.com/translate_f 456/504
16/12/2021 10:52 Sem título
# enviar mensagem para o grupo da sala
async_to_sync (self.channel_layer.group_send) (
self.room_group_name,
{
'tipo': 'chat_message',
'mensagem': mensagem,
}
)
[ 489 ]
Página 515
Figura 13.6: A página da sala de chat com mensagens enviadas de diferentes janelas do navegador
https://translate.googleusercontent.com/translate_f 457/504
16/12/2021 10:52 Sem título
importar json
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
[ 490 ]
Página 516
Capítulo 13
https://translate.googleusercontent.com/translate_f 458/504
16/12/2021 10:52 Sem título
[ 491 ]
Página 517
Agora você importa o módulo de fuso horário fornecido pelo Django. No conectar ()
método do consumidor, você recupera o usuário atual do escopo com self.
escopo ['usuário'] e armazená-los em um novo atributo de usuário do consumidor. Quando o
consumidor recebe uma mensagem através do WebSocket, ele obtém a hora atual usando
timezone.now () e passa o usuário atual e a data e hora no formato ISO 8601
junto com a mensagem do evento enviada ao grupo de canais.
1. Você agora converte a data e hora recebida na mensagem para uma data de JavaScript
objeto e formate-o com uma localidade específica.
[ 492 ]
https://translate.googleusercontent.com/translate_f 459/504
16/12/2021 10:52 Sem título
Página 518
Capítulo 13
Em seguida, abra uma segunda janela do navegador no modo anônimo para evitar o uso do
mesma sessão. Faça login com um usuário diferente, também matriculado no mesmo curso, e envie
uma mensagem.
Você poderá trocar mensagens usando dois usuários diferentes e ver o usuário
e hora, com uma distinção clara entre mensagens enviadas pelo usuário e mensagens
enviado por outros. A conversa entre dois usuários deve ser semelhante ao
seguinte:
Figura 13.7: A página da sala de chat com mensagens de duas sessões de usuário diferentes
[ 493 ]
https://translate.googleusercontent.com/translate_f 460/504
16/12/2021 10:52 Sem título
Página 519
importar json
from channels.generic.websocket import AsyncWebsocketConsumer
from asgiref.sync import async_to_sync
de django.utils importar fuso horário
[ 494 ]
Página 520
https://translate.googleusercontent.com/translate_f 461/504
16/12/2021 10:52 Sem título
Capítulo 13
[ 495 ]
Página 521
<h3>
<a href="{% url "chat:course_chat_room" object.id %}">
https://translate.googleusercontent.com/translate_f 462/504
16/12/2021 10:52 Sem título
Sala de chat do curso
</a>
</h3>
</div>
Abra o navegador e acesse qualquer curso no qual o aluno está matriculado para visualizar o
Conteúdos do curso. A barra lateral agora conterá um link para a sala de bate-papo do curso que aponta
para a visualização da sala de bate-papo do curso. Se clicar nele, você entrará na sala de chat.
Figura 13.8: A página de detalhes do curso, incluindo um link para a sala de bate-papo do curso
Resumo
Neste capítulo, você aprendeu como criar um servidor de bate-papo usando canais. Você
implementou um cliente e consumidor WebSocket. Você também ativou a comunicação
entre os consumidores usando uma camada de canal com Redis e modificou o consumidor
para ser totalmente assíncrono.
[ 496 ]
Página 522
https://translate.googleusercontent.com/translate_f 463/504
16/12/2021 10:52 Sem título
Going Live
Canais. Agora que criou uma plataforma de e-learning totalmente funcional, você
precisa configurar um ambiente de produção em um servidor online para que possa ser
acessado pela Internet. Até agora, você tem trabalhado em um desenvolvimento
14
No capítulo anterior, você construiu um servidor de chat em tempo real para alunos usando Django
ambiente, usando o servidor de desenvolvimento Django para executar seu site. Neste capítulo,
você aprenderá como configurar um ambiente de produção que seja capaz de atender ao seu
Projete Django de maneira segura e eficiente.
[ 497 ]
Página 523
Going Live
definições/
__init__.py
base.py
https://translate.googleusercontent.com/translate_f 464/504
16/12/2021 10:52 Sem título
local.py
pro.py
com o seguinte:
Você moveu seus arquivos de configurações para um diretório um nível abaixo, então você precisa de BASE_
DIR para apontar para o diretório pai para estar correto. Você consegue isso apontando para
o diretório pai com os.pardir .
de importação .base *
DEBUG = True
DATABASES = {
[ 498 ]
Página 524
Capítulo 14
'padrão': {
'ENGINE': 'django.db.backends.sqlite3',
'NOME': os.path.join (BASE_DIR, 'db.sqlite3'),
}
}
Este é o arquivo de configurações para seu ambiente local. Você importa todas as configurações definidas
no arquivo base.py e você só define configurações específicas para este ambiente. Você
copie as configurações DEBUG e DATABASES do arquivo base.py , pois eles irão
ser definido por ambiente. Você pode remover as configurações DATABASES e DEBUG de
o arquivo de configurações base.py.
Edite o arquivo settings / pro.py e faça com que tenha a seguinte aparência:
de importação .base *
DEBUG = False
ADMINS = (
('Antonio M', 'email@mydomain.com'),
)
ALLOWED_HOSTS = ['*']
DATABASES = {
https://translate.googleusercontent.com/translate_f 465/504
16/12/2021 10:52 Sem título
'padrão': {
}
}
Essas são as configurações do ambiente de produção. Vamos dar uma olhada em cada
deles:
• DEBUG : Definir DEBUG como False deve ser obrigatório para qualquer produção
ambiente. Não fazer isso resultará nas informações de rastreamento e
dados de configuração sensíveis sendo expostos a todos.
• ADMINS : quando DEBUG é False e uma visualização gera uma exceção, todas as informações
será enviado por e-mail para as pessoas listadas na configuração ADMINS . Certificar-se
que você substitua a tupla de nome / e-mail por suas próprias informações.
• ALLOWED_HOSTS : Django só permitirá que os hosts incluídos nesta lista
servir ao aplicativo. Esta é uma medida de segurança. Você inclui o asterisco
símbolo, * , para se referir a todos os nomes de host. Você limitará os nomes de host que podem
ser usado para servir o aplicativo posteriormente.
• BANCOS DE DADOS : Você apenas mantém esta configuração vazia. Vamos cobrir o
configuração do banco de dados para produção posterior.
[ 499 ]
Página 525
Going Live
Você organizou com êxito as configurações para lidar com vários ambientes.
Usando PostgreSQL
Ao longo deste livro, você usou principalmente o banco de dados SQLite. SQLite é simples
e rápido de configurar, mas para um ambiente de produção, você precisará de mais
banco de dados poderoso, como PostgreSQL, MySQL ou Oracle. Voce ja aprendeu
como instalar o PostgreSQL e configurar um banco de dados PostgreSQL no Capítulo 3 , Extensão
https://translate.googleusercontent.com/translate_f 466/504
16/12/2021 10:52 Sem título
Seu aplicativo de blog . Se você precisa instalar o PostgreSQL, você pode ler o Instalando
Seção PostgreSQL do Capítulo 3 .
Vamos criar um usuário PostgreSQL. Abra o shell e execute os seguintes comandos para
criar um usuário de banco de dados:
su postgres
[ 500 ]
Página 526
Capítulo 14
Em seguida, edite o arquivo settings / pro.py e modifique a configuração DATABASES para torná-lo
olhe o seguinte:
DATABASES = {
'padrão': {
'ENGINE': 'django.db.backends.postgresql',
'NOME': 'educa',
'USUÁRIO': 'educa',
'SENHA': '*****',
}
}
Você verá a saída sem erros, mas vários avisos. Isso significa que o cheque foi
bem-sucedido, mas você deve ler os avisos para ver se há algo mais
você pode fazer para tornar seu projeto seguro para produção. Não vamos nos aprofundar
nisso, mas tenha em mente que você deve verificar seu projeto antes da produção
use para procurar quaisquer questões relevantes.
Quando você gera um novo projeto usando o comando startproject , o Django cria
um arquivo wsgi.py dentro do diretório do projeto. Este arquivo contém um aplicativo WSGI
chamável, que é um ponto de acesso ao seu aplicativo.
[ 501 ]
Página 527
Going Live
WSGI é usado para executar seu projeto com o servidor de desenvolvimento Django
e implantar seu aplicativo com o servidor de sua escolha em uma produção
ambiente.
Instalando uWSGI
Ao longo deste livro, você tem usado o servidor de desenvolvimento Django para
execute projetos em seu ambiente local. No entanto, você precisa de um servidor web real para
implementar seu aplicativo em um ambiente de produção.
uWSGI é um servidor de aplicativos Python extremamente rápido. Ele se comunica com o seu
Aplicação Python usando a especificação WSGI. uWSGI traduz solicitações da web
em um formato que seu projeto Django possa processar.
Para construir uWSGI, você precisará de um compilador C, como gcc ou clang . Dentro
um ambiente Linux, você pode instalar um compilador C com o comando apt-get
instalar essencial para compilar .
Se você estiver usando o macOS, pode instalar o uWSGI com o pacote Homebrew
gerenciador usando o comando brew install uwsgi .
Se você deseja instalar o uWSGI no Windows, você precisará do Cygwin: https: // www.
cygwin.com . No entanto, é desejável usar uWSGI em ambientes baseados em UNIX.
Configurando uWSGI
Você pode executar uWSGI a partir da linha de comando. Abra o shell e execute o seguinte
comando do diretório do projeto educa :
--uid = 1000 \
--virtualenv = / home / env / educa /
[ 502 ]
https://translate.googleusercontent.com/translate_f 468/504
16/12/2021 10:52 Sem título
Página 528
Capítulo 14
Você pode ter que prefixar sudo a este comando se você não tiver o necessário
permissões. Você também pode precisar adicionar a opção --plugin = python3 se o
módulo não é carregado por padrão.
Com este comando, você pode executar uWSGI em seu host local com o seguinte
opções:
Abra http://127.0.0.1:8000/ em seu navegador. Você deve ver uma tela como o
seguinte:
Você pode ver o HTML renderizado que corresponde à visualização da lista do curso, mas não
Folhas de estilo CSS ou imagens estão sendo carregadas. A razão para isso é que você não
configurar uWSGI para servir arquivos estáticos. Você vai configurar o serviço de arquivos estáticos no
ambiente de produção posteriormente neste capítulo.
uWSGI permite definir uma configuração personalizada em um arquivo .ini . Isso é mais
conveniente do que passar opções pela linha de comando.
config /
uwsgi.ini
Histórico/
[ 503 ]
https://translate.googleusercontent.com/translate_f 469/504
16/12/2021 10:52 Sem título
Página 529
Going Live
[uwsgi]
# variáveis
projectname = educa
base = / casa / projetos / educa
# configuração
mestre = verdadeiro
virtualenv = / home / env /% (nome do projeto)
pythonpath =% (base)
chdir =% (base)
env = DJANGO_SETTINGS_MODULE =% (nome do projeto) .settings.pro
módulo =% (nome do projeto) .wsgi: aplicativo
socket = /tmp/%(projectname).sock
chmod-socket = 666
Estas são variáveis personalizadas que você usará nas opções uWSGI. Você pode definir
qualquer outra variável de que você goste, desde que os nomes sejam diferentes das opções do uWSGI.
• chdir : o caminho para o diretório do seu projeto, de modo que uWSGI mude para aquele
diretório antes de carregar o aplicativo.
[ 504 ]
Página 530
https://translate.googleusercontent.com/translate_f 470/504
16/12/2021 10:52 Sem título
Capítulo 14
Agora, você pode executar uWSGI com sua configuração personalizada usando este comando:
Você não será capaz de acessar sua instância uWSGI de seu navegador agora, uma vez que
está passando por um soquete. Vamos completar o ambiente de produção.
Instalando NGINX
Quando você está servindo um site, você tem que servir conteúdo dinâmico, mas você também
precisam servir arquivos estáticos, como folhas de estilo CSS, arquivos JavaScript e imagens. Enquanto
uWSGI é capaz de servir arquivos estáticos, adiciona uma sobrecarga desnecessária ao HTTP
solicitações e, portanto, é recomendável configurar um servidor web, como NGINX,
na frente dele.
Se você estiver usando o macOS, pode instalar o NGINX usando o comando brew install
nginx .
sudo nginx
[ 505 ]
Página 531
Going Live
https://translate.googleusercontent.com/translate_f 471/504
16/12/2021 10:52 Sem título
Se você vir esta tela, o NGINX foi instalado com sucesso. 80 é a porta padrão
Configuração NGINX.
O ambiente de produção
O diagrama a seguir mostra o ciclo de solicitação / resposta da produção
ambiente que você está configurando:
4. O Django retorna uma resposta HTTP que é passada de volta ao NGINX, que
por sua vez, passa de volta para o navegador do cliente
Configurando NGINX
Crie um novo arquivo dentro do diretório config / e nomeie-o como nginx.conf . Adicione o
seguinte código para ele:
[ 506 ]
Página 532
Capítulo 14
servidor {
escute 80;
server_name www.educaproject.com educaproject.com;
access_log off;
https://translate.googleusercontent.com/translate_f 472/504
16/12/2021 10:52 Sem título
error_log /home/projects/educa/logs/nginx_error.log;
localização / {
inclua / etc / nginx / uwsgi_params;
uwsgi_pass educa;
}
}
[ 507 ]
Página 533
Going Live
http {
incluem /home/projects/educa/config/nginx.conf;
# ...
}
https://translate.googleusercontent.com/translate_f 473/504
16/12/2021 10:52 Sem título
Sempre que quiser interromper o NGINX, você pode fazê-lo normalmente com o seguinte
comando:
Se você quiser parar rapidamente o NGINX, em vez de sair, use o sinal de parada . O desistir
sinal de espera por processos de trabalho para terminar servindo solicitações atuais, enquanto a paragem
sinal para NGINX abruptamente.
Como você está usando um nome de domínio de amostra, é necessário redirecioná-lo para seu host local.
Edite seu arquivo / etc / hosts e adicione a seguinte linha a ele:
Ao fazer isso, você está roteando ambos os nomes de host para seu servidor local. Em uma produção
servidor, você não precisará fazer isso, pois terá um endereço IP fixo e você
irá apontar o seu nome de host para o seu servidor na configuração DNS do seu domínio.
Agora você pode restringir os hosts que podem servir ao seu projeto Django. Edite o
arquivo de configurações de produção settings / pro.py do seu projeto e altere o ALLOWED_
Configuração de HOSTS , como segue:
ALLOWED_HOSTS = [ 'educaproject.com', 'www.educaproject.com' ]
Django agora só servirá seu aplicativo se ele estiver sendo executado em qualquer um desses
nomes de host. Você pode ler mais sobre a configuração ALLOWED_HOSTS emhttps: // docs.
djangoproject.com/en/3.0/ref/settings/#allowed-hosts.
[ 508 ]
Página 534
Capítulo 14
Edite o arquivo settings / base.py e adicione a seguinte linha logo abaixo de STATIC_
Configuração de URL :
Cada aplicativo em seu projeto Django pode conter arquivos estáticos em um arquivo estático /
diretório. Django fornece um comando para coletar arquivos estáticos de todos os aplicativos
em um único local. Isso simplifica a configuração para servir arquivos estáticos na produção.
O collectstatic comando coleta os arquivos estáticos a partir de todas as aplicações do
projeto no caminho definido em STATIC_ROOT .
https://translate.googleusercontent.com/translate_f 474/504
16/12/2021 10:52 Sem título
Arquivos localizados no diretório estático / de cada aplicativo presente no
A configuração INSTALLED_APPS foi copiada para global / educa / static / project
diretório.
servidor {
escute 80;
server_name www.educaproject.com educaproject.com;
access_log off;
error_log /home/projects/educa/logs/nginx_error.log;
localização / {
inclua / etc / nginx / uwsgi_params;
[ 509 ]
Página 535
Going Live
uwsgi_pass educa;
}
localização / estático / {
alias / home / projects / educa / static /;
}
localização / mídia / {
pseudônimo / home / projects / educa / media /;
}
}
https://translate.googleusercontent.com/translate_f 475/504
16/12/2021 10:52 Sem título
Figura 14.4: O ciclo de solicitação / resposta do ambiente de produção, incluindo arquivos estáticos
Os arquivos nos caminhos / static / e / media / agora são servidos pelo NGINX diretamente,
em vez de ser encaminhado para uWSGI. As solicitações para qualquer outro caminho ainda são passadas por
NGINX para uWSGI por meio do soquete UNIX.
[ 510 ]
Página 536
Capítulo 14
Recursos estáticos, como imagens e folhas de estilo CSS, agora são carregados corretamente.
As solicitações HTTP para arquivos estáticos agora estão sendo atendidas por NGINX diretamente, em vez de
sendo encaminhado para uWSGI.
Excelente! Você configurou com êxito o NGINX para servir arquivos estáticos.
https://translate.googleusercontent.com/translate_f 476/504
16/12/2021 10:52 Sem título
sudo openssl
educa.key req
-out ssl-x509 -nodes -days 365 -newkey rsa: 2048 -keyout ssl /
/ educa.crt
[ 511 ]
Página 537
Going Live
Você está gerando uma chave privada e um certificado SSL / TLS de 2.048 bits válido para
um ano. Você será solicitado a inserir os dados da seguinte forma:
Você pode preencher os dados solicitados com suas próprias informações. O mais importante
campo é o nome comum . Você deve especificar o nome de domínio do certificado.
Você usa educaproject.com . Isso irá gerar, dentro do diretório ssl / , um educa.
arquivo de chave privada e um arquivo educa.crt , que é o certificado real.
servidor {
ouço 80;
ouço 443 ssl;
ssl_certificate /home/projects/educa/ssl/educa.crt;
ssl_certificate_key /home/projects/educa/ssl/educa.key;
nome do servidor www.educaproject.com educaproject.com;
# ...
}
Com o código anterior, seu servidor agora escuta HTTP por meio da porta 80
e HTTPS por meio da porta 443 . Você indica o caminho para o certificado SSL / TLS
com ssl_certificate e a chave do certificado com ssl_certificate_key .
[ 512 ]
https://translate.googleusercontent.com/translate_f 477/504
16/12/2021 10:52 Sem título
Página 538
Capítulo 14
Esta tela pode variar dependendo do seu navegador. Ele alerta que seu site é
não usar um certificado confiável ou válido; o navegador não pode verificar a identidade do seu
local. Isso ocorre porque você assinou seu próprio certificado em vez de obter um de
uma autoridade de certificação ( CA ) confiável . Quando você possui um domínio real, você pode aplicar
para uma CA confiável emitir um certificado SSL / TLS para ele, para que os navegadores possam verificar seu
identidade. Se você deseja obter um certificado confiável para um domínio real, você pode consultar
para o projeto Let's Encrypt , criado pela Linux Foundation. É uma CA sem fins lucrativos
que simplifica a obtenção e renovação de certificados SSL / TLS confiáveis gratuitamente. Você
pode encontrar mais informações em https://letsencrypt.org .
Clique no link ou botão que fornece informações adicionais e opte por visitar
o site, ignorando os avisos. O navegador pode solicitar que você adicione uma exceção
para este certificado ou verifique se você confia nele. Se você estiver usando o Chrome, você pode
não vejo nenhuma opção para acessar o site. Se for este o caso, digite thisisunsafe
ou badidea diretamente no Chrome na mesma página de aviso. O Chrome irá então carregar
o site. Observe que você faz isso com seu próprio certificado emitido; não confie em nenhum
certificado desconhecido ou ignorar as verificações de certificado SSL / TLS do navegador para outros
domínios.
[ 513 ]
https://translate.googleusercontent.com/translate_f 478/504
16/12/2021 10:52 Sem título
Página 539
Going Live
Ao acessar o site, você verá que o navegador exibe um ícone de cadeado próximo a
o URL, da seguinte maneira:
Figura 14.7: A barra de endereço do navegador, incluindo um ícone de cadeado de conexão segura
Se você clicar no ícone de cadeado, os detalhes do certificado SSL / TLS serão exibidos da seguinte forma:
Nos detalhes do certificado, você pode ver que é um certificado autoassinado e você pode ver seu
data de validade. Agora você está servindo seu site com segurança.
SECURE_SSL_REDIRECT = Verdadeiro
CSRF_COOKIE_SECURE = Verdadeiro
[ 514 ]
Página 540
https://translate.googleusercontent.com/translate_f 479/504
16/12/2021 10:52 Sem título
Capítulo 14
Django agora redirecionará as solicitações HTTP para HTTPS e cookies para proteção CSRF
agora estará seguro.
servidor {
escute 80;
server_name www.educaproject.com educaproject.com;
return 301 https: //educaproject.com$request_uri;
}
servidor {
ouço 443 ssl;
ssl_certificate /home/projects/educa/ssl/educa.crt;
ssl_certificate_key /home/projects/educa/ssl/educa.key;
server_name www.educaproject.com educaproject.com;
access_log off;
error_log /home/projects/educa/logs/nginx_error.log;
localização / {
inclua / etc / nginx / uwsgi_params;
uwsgi_pass educa;
}
localização / estático / {
alias / home / projects / educa / static /;
}
localização / mídia / {
pseudônimo / home / projects / educa / media /;
}
}
[ 515 ]
Página 541
Going Live
Neste código, você remove a diretiva listen 80; do bloco de servidor original ,
para que a plataforma só esteja disponível por meio de SSL / TLS (porta 443 ). No topo de
https://translate.googleusercontent.com/translate_f 480/504
16/12/2021 10:52 Sem título
bloco de servidor original , você adiciona um bloco de servidor adicional que só escuta
porta 80 e redireciona todas as solicitações HTTP para HTTPS. Para conseguir isso, você retorna
um código de resposta HTTP 301 (redirecionamento permanente) que redireciona para https: //
versão do URL solicitado.
Agora você está redirecionando todo o tráfego HTTP para HTTPS usando NGINX.
Daphne é um servidor HTTP, HTTP2 e WebSocket para ASGI desenvolvido para servir
Canais. Você pode executar Daphne junto com uWSGI para servir tanto ASGI quanto WSGI
aplicativos de forma eficiente.
Django 3 oferece suporte a WSGI e ASGI, mas ainda não oferece suporte a WebSockets.
Portanto, você vai editar o arquivo asgi.py do projeto educa para usar
Canais.
importar os
importar django
from channels.routing import get_default_application
[ 516 ]
Página 542
Capítulo 14
https://translate.googleusercontent.com/translate_f 481/504
16/12/2021 10:52 Sem título
com o ambiente de produção usando o seguinte comando:
exportar DJANGO_SETTINGS_MODULE = educa.settings.pro
2020-02-11 00: 49: 44.223 INFO HTTP / 2 support not enabled (instale o
http2 e tls Twisted extras)
2020-02-11 00: 49: 44.223 INFO Configurando endpoint unix: / tmp / daphne.
meia
A saída mostra que Daphne está sendo executado com êxito em um soquete UNIX.
[ 517 ]
Página 543
Going Live
Edite o arquivo config / nginx.conf do projeto educa e faça com que tenha a seguinte aparência:
a montante daphne {
servidor unix: /tmp/daphne.sock;
}
https://translate.googleusercontent.com/translate_f 482/504
16/12/2021 10:52 Sem título
servidor {
escute 80;
server_name www.educaproject.com educaproject.com;
return 301 https: //educaproject.com$request_uri;
}
servidor {
ouço 443 ssl;
ssl_certificate /home/projects/educa/ssl/educa.crt;
ssl_certificate_key /home/projects/educa/ssl/educa.key;
access_log off;
error_log /home/projects/educa/logs/nginx_error.log;
localização / {
inclua / etc / nginx / uwsgi_params;
uwsgi_pass educa;
}
localização / ws / {
proxy_http_version 1.1;
proxy_set_header Atualizar $ http_upgrade;
proxy_set_header Conexão "upgrade";
proxy_redirect off;
[ 518 ]
Página 544
Capítulo 14
localização / estático / {
alias / home / projects / educa / static /;
}
localização / mídia / {
pseudônimo / home / projects / educa / media /;
}
}
Nesta configuração, você configura um novo upstream chamado daphne , que aponta para um
socket criado por Daphne. No bloco do servidor , você configura o local / ws / para
encaminhar pedidos para Daphne. Você usa a diretiva proxy_pass para passar solicitações para
Daphne e você incluem algumas diretivas de proxy adicionais.
Com essa configuração, o NGINX passará qualquer solicitação de URL que comece com / ws /
prefixo para Daphne e o resto para uWSGI, exceto para arquivos em / static / ou /
mídia / caminhos, que serão servidos diretamente pelo NGINX.
https://translate.googleusercontent.com/translate_f 483/504
16/12/2021 10:52 Sem título
NGINX é executado na frente de uWSGI e Daphne como um servidor proxy reverso. Faces NGINX
a Web e passa as solicitações para o servidor de aplicativos (uWSGI ou Daphne) com base
em seu prefixo de caminho. Além disso, o NGINX também serve arquivos estáticos e redireciona não
pedidos seguros para proteger os outros. Esta configuração reduz o tempo de inatividade, consome menos servidor
recursos e fornece maior desempenho e segurança.
[ 519 ]
Página 545
Going Live
Use seu navegador para criar um curso de amostra com um usuário instrutor, faça login com um
usuário que está inscrito no curso, e abra https://educaproject.com/chat/
quarto / 1 / com seu navegador. Você deve ser capaz de enviar e receber mensagens como
o seguinte exemplo:
Figura 14.10: Mensagens da sala de bate-papo do curso servidas com NGINX e Daphne
Daphne está funcionando corretamente e o NGINX está passando solicitações para ele. Todas as conexões
são protegidos por SSL / TLS.
Parabéns! Você construiu uma pilha personalizada pronta para produção usando NGINX,
uWSGI e Daphne. Você poderia fazer mais otimização para desempenho adicional
e segurança aprimorada por meio de definições de configuração em NGINX, uWSGI e
Daphne. No entanto, essa configuração de produção é um ótimo começo!
https://translate.googleusercontent.com/translate_f 484/504
16/12/2021 10:52 Sem título
Evite adicionar processamento caro ao middleware, uma vez que eles são
executado em cada solicitação.
[ 520 ]
Página 546
Capítulo 14
resposta de retorno
return middleware
Se algum middleware retornar uma resposta sem chamar seu get_response chamável,
ele causa um curto-circuito no processo; nenhum middleware adicional é executado (também não a visualização),
e a resposta retorna pelas mesmas camadas pelas quais a solicitação passou.
https://translate.googleusercontent.com/translate_f 485/504
16/12/2021 10:52 Sem título
Você pode encontrar mais informações sobre middleware em https: // docs.
djangoproject.com/en/3.0/topics/http/middleware/ .
[ 521 ]
Página 547
Going Live
Crie um novo arquivo dentro do diretório do aplicativo de cursos e nomeie-o como middleware.
py . Adicione o seguinte código a ele:
return middleware
[ 522 ]
https://translate.googleusercontent.com/translate_f 486/504
16/12/2021 10:52 Sem título
Página 548
Capítulo 14
1. Você obtém o nome do host que está sendo usado na solicitação e o divide em
partes. Por exemplo, se o usuário estiver acessando mycourse.educaproject.com ,
você gera a lista ['mycourse', 'educaproject', 'com'] .
2. Você verifica se o nome do host inclui um subdomínio verificando se
a divisão gerou mais de dois elementos. Se o nome do host inclui um
subdomínio, e este não é www , você tenta pegar o curso com o slug
fornecido no subdomínio.
3. Se um curso não for encontrado, você gerará uma exceção HTTP 404 . Caso contrário, você
redirecionar o navegador para a URL de detalhes do curso.
MIDDLEWARE = [
# ...
'cursos.middleware.subdomain_course_middleware',
]
Lembre-se de que os nomes de host permitidos para servir ao seu projeto Django são especificados em
a configuração ALLOWED_HOSTS . Vamos mudar esta configuração para que qualquer subdomínio possível
de educaproject.com tem permissão para servir seu aplicativo.
ALLOWED_HOSTS = [ '.educaproject.com' ]
[ 523 ]
https://translate.googleusercontent.com/translate_f 487/504
16/12/2021 10:52 Sem título
Página 549
Going Live
com o seguinte:
127.0.0.1 django.educaproject.com
Pare e inicie o uWSGI novamente e recarregue o NGINX com o seguinte comando para
acompanhe a configuração mais recente:
Você pode aprender mais sobre comandos de gerenciamento personalizados em https: // docs.
djangoproject.com/en/3.0/howto/custom-management-commands/.
[ 524 ]
Página 550
https://translate.googleusercontent.com/translate_f 488/504
16/12/2021 10:52 Sem título
Capítulo 14
gestão/
__init__.py
comandos /
__init__.py
matricula_reminder.py
[ 525 ]
Página 551
Going Live
• Você inclui um atributo de ajuda . Este atributo fornece uma breve descrição de
o comando que é impresso se você executar o comando python manage.py
ajudar a register_reminder .
• Use o método add_arguments () para adicionar o argumento denominado --days .
Este argumento é usado para especificar o número mínimo de dias que um usuário tem para
estar inscrito, sem ter se inscrito em nenhum curso, para receber o
lembrete.
• O comando handle () contém o comando real. Você começa os dias
atributo analisado a partir da linha de comando. Você usa o utilitário de fuso horário
fornecido pelo Django para recuperar a data atual ciente do fuso horário com
timezone.now (). date () . (Você pode definir o fuso horário para o seu projeto com
a configuração TIME_ZONE .) Você recupera os usuários que foram registrados
por mais do que os dias especificados e ainda não estão inscritos em nenhum curso.
Você consegue isso anotando o QuerySet com o número total de cursos
cada usuário está inscrito em. Você gera o e-mail de lembrete para cada usuário e
acrescente-o à lista de e - mails . Finalmente, você envia os e-mails usando o send_
função mass_mail () , que é otimizada para abrir uma única conexão SMTP
para enviar todos os e-mails, em vez de abrir uma conexão por e-mail enviado.
Você criou seu primeiro comando de gerenciamento. Abra o shell e execute o seu
comando:
Se você não tem um servidor SMTP local em execução, você pode dar uma olhada no Capítulo 2 ,
Aprimorando seu blog com recursos avançados , onde você definiu as configurações de SMTP
para seu primeiro projeto Django. Como alternativa, você pode adicionar a seguinte configuração a
o arquivo settings.py para fazer os emails de saída do Django para a saída padrão durante
desenvolvimento:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Vamos agendar seu comando de gerenciamento para que o servidor o execute todos os dias às 8
am Se você estiver usando um sistema baseado em UNIX, como Linux ou macOS, abra o shell
e execute crontab -e para editar seu crontab . Adicione a seguinte linha a ele:
Se você não está familiarizado com o cron , pode encontrar uma introdução ao cron em http: //
www.unixgeeks.org/security/newbie/unix/cron-1.html .
[ 526 ]
Página 552
Capítulo 14
Se estiver usando o Windows, você pode agendar tarefas usando o Agendador de Tarefas. Você
pode encontrar mais informações sobre ele em https://docs.microsoft.com/en-us/
windows / win32 / taskschd / task-scheduler-start-page .
periodic-tasks.html .
Django também inclui um utilitário para chamar comandos de gerenciamento usando Python. Você pode
execute comandos de gerenciamento de seu código da seguinte maneira:
Parabéns! Agora você pode criar comandos de gerenciamento personalizados para o seu
aplicativos e agendá-los quando necessário.
Resumo
Neste capítulo, você configurou um ambiente de produção usando NGINX,
uWSGI e Daphne. Você protegeu seu ambiente por meio de SSL / TLS. Você
também implementou um middleware customizado e você aprendeu a criar um middleware customizado
comandos de gerenciamento.
[ 527 ]
Página 554
553
Outros livros
Você pode desfrutar
https://translate.googleusercontent.com/translate_f 491/504
16/12/2021 10:52 Sem título
Se você gostou deste livro, pode estar interessado nestes outros livros de Packt:
ISBN: 978-1-83921-310-6
[ 529 ]
Página 555
Rowel Atienza
ISBN: 978-1-83882-165-4
● Use técnicas de maximização de informações mútuas para realizar tarefas não supervisionadas
Aprendendo
● Use a segmentação para identificar a classe em termos de pixels de cada objeto em uma imagem
● Identifique a caixa delimitadora e a classe de objetos em uma imagem usando o objeto
detecção
● Aprenda os blocos de construção para técnicas avançadas - MLPss, CNN e RNNs
● Compreenda as redes neurais profundas - incluindo ResNet e DenseNet
https://translate.googleusercontent.com/translate_f 492/504
16/12/2021 10:52 Sem título
● Compreenda e crie modelos autorregressivos - codificadores automáticos, VAEs e GANs
● Descubra e implemente métodos de aprendizagem de reforço profundo
[ 530 ]
Página 556
https://translate.googleusercontent.com/translate_f 493/504
16/12/2021 10:52 Sem título
[ 531 ]
Página 558
557
Índice
Símbolos Paginação AJAX
adicionando, à exibição de lista 181 -185
__str __ () método 12 Solicitações AJAX
__unicode __ () método 13 desempenho, com jQuery 176 -179
com falsificação de solicitação entre sites
UMA (CSRF) 174 , 175
Visualização AJAX
modelo abstrato 366
edifício, para seguir os usuários 196 -198
fluxo de atividade
Configuração ALLOWED_HOSTS
exibindo 206 , 207
link de referência 132 , 508
método add (), parâmetros
aplicação 9
override_quantity 242
classes de configuração de aplicativos
produto 242
cerca de 213 -215
quantidade 242
link de referência 213
site de administração
aplicativos assíncronos
criando, para os modelos 15
ASGI, usando 471
ações personalizadas, adicionando 284 -286
consumidor assíncrono
Site de administração do Django 16
modificando 494 , 495
estendendo, com visualizações personalizadas 287 , 290 Interface de gateway de servidor assíncrono
modelos, adicionando 17 , 18
(ASGI) 2 , 516
modelos, personalizando 19 , 21
tarefas assíncronas
superusuário, criando 15
adicionando, ao aplicativo 265 -267
modelos de administração
lançamento, com aipo 263
link de referência 290 backend de autenticação, métodos
agregação
autenticar () 129
link de referência 64
get_user () 129
funções de agregação
valor médio (médio) 64
valor de contagem (contagem) 64
B
valor máximo (Max) 64 classes base, Django
valor mínimo (Min) 64
https://translate.googleusercontent.com/translate_f 494/504
16/12/2021 10:52 Sem título
link de referência 72 Formulário
Formulário 40Modelo 40
Ações AJAX esquema de dados do blog
adicionando, com jQuery 172 , 173 aplicativo, ativando 13
Solicitação GET AJAX, cenário projetando 10 , 11
resposta, com dados 184 migrações, aplicando 13 , 14
resposta, sem conteúdo 184 migrações, criando 13 , 14
[ 533 ]
Página 559
[ 534 ]
https://translate.googleusercontent.com/translate_f 495/504
16/12/2021 10:52 Sem título
Página 560
[ 535 ]
https://translate.googleusercontent.com/translate_f 496/504
16/12/2021 10:52 Sem título
Página 561
[ 536 ]
Página 562
https://translate.googleusercontent.com/translate_f 497/504
16/12/2021 10:52 Sem título
[ 537 ]
Página 563
https://translate.googleusercontent.com/translate_f 498/504
16/12/2021 10:52 Sem título
ForeignKey
link de referência 12 link de referência 91
localização de formato Homebrew
cerca de 344 , 345 link de download 433
link de referência 345
campos de formulário eu
limpeza 154 , 155
site de bookmarking de imagens
link de referência 41
formulários criando 150
modelo de imagem, edifício 150 , 151
criando, com Django 40 , 41
modelo de imagem, registrando-se na administração
manuseio, nas vistas 41 , 42
site 153
renderização, em modelos 45 -49
relacionamentos muitos para muitos, criando 152
Conjuntos de formulários
link de referência 392 modelo de imagem
edifício 150 , 151
pesquisa de texto completo
registrando, no site de administração 153
adicionando, ao blog 82
classificação de imagem
link de referência 90
armazenamento, no Redis 220 , 222
motor de busca de texto completo 91
imagens
Traduções fuzzy 331
vista de detalhes, criando 168 , 170
miniaturas de imagens
G
criando, com miniaturas fáceis 170 , 171
aplicativo de fluxo de atividade genérico internacionalização
fluxo de atividade, exibindo 206 , 207 cerca de 314
edifício 198 , 199 adicionando 314
framework contenttypes, usando 200 , 201 comandos de gerenciamento 316
ações duplicadas, evitando na atividade projeto, preparando 317 , 318
fluxo 204 , 205 configurações 315 , 316
relações genéricas, adicionando ao modelo 201 -204 com Django 315
QuerySets, otimizando 207 visualizações de itens
modelos, criando para ações 208 -210 armazenamento, com Redis 215 , 218, 219
as ações do usuário, adicionando à atividade
fluxo 205 , 206 J
mixins e visualizações genéricas
jQuery
link de referência 447
relações genéricas link de download 174
carregando 173
adicionando, ao modelo 201 -204
link de referência 160
método get_form () 398
usado, para adicionar ações AJAX 172 , 173
método get () 398
usado, para construir o bookmarklet 160 -167
método get_model () 397
usado, para realizar solicitações AJAX 176 -179
Captcha do Gmail, desativando
Seletores jQuery
link de referência 44
link de referência 164
Implementação OAuth2 do Google
link de referência 142 Cookie JS
link de referência 175
[ 538 ]
Página 564
https://translate.googleusercontent.com/translate_f 499/504
16/12/2021 10:52 Sem título
edifício 26 herança de modelo
URL canônico, para modelos 29 modelo abstrato 366
criando 26 , 27 multi-mesa modelo de herança 367
criando, para perfis de usuário 191 -195 modelo de proxy 367
Padrões de URL, adicionando 28 , 29 usando 366
localização gerentes de modelo
cerca de 314 criando 25 , 26
adicionando 314 modelos
configurações 315 , 316 traduzindo, com django-parler 336
API de cache de baixo nível campos múltiplos
usando 436 -438 procurando 84
vários subdomínios
M servindo, com NGINX 523 , 524
Herança de modelo multi-mesa 366
gerentes
trabalhando com 21
N
relacionamentos muitos para muitos
criando 152 Binários NGINX, para Windows
criando, com modelo intermediário 188 -191 link de referência 505
link de referência 61 , 152 Documentação NGINX
relacionamentos muitos para um link de referência 507
link de referência 51
formato de redução O
link de referência 74
Back-end Memcached mapeador objeto-relacional (ORM) 21
adicionando, ao projeto 434 projeto de loja online
link de download 433 modelos de catálogo, criando 233 -237
instalando 433 criando 226
monitoramento 435 funcionalidades 226
arquivo de mensagem 315 modelos de catálogo de produtos, criando 227 -229
quadro de mensagens modelos de catálogo de produtos, registrando-se em
link de referência 128 site de administração 229
usando 125 -128 visualizações de catálogo do produto, a construção de 230 -232
middleware ordens
cerca de 96 exportando, para arquivos CSV 284
link de referência 521
migrar comando de gerenciamento 6
migrações
[ 539 ]
Página 565
P link de referência 82
Carteiro
paginação link de referência 449
adicionando 34 , 35 método post () 399
trabalhando 34 postagens por semelhança
analisadores recuperando 64 -66
link de referência 447 postagens, via e-mail
conversores de caminho e-mails, enviando com Django 43 -45
link de referência 28 formulários, criando com Django 40 , 41
autorização de pagamento formulários, manuseio nas vistas 41 , 42
link de referência 283 formulários, renderização em modelos 45 -49
Indústria de cartões de pagamento (PCI) 270 compartilhando 40
Gateway de pagamento prefetch_related ()
Braintree, integrando-se com o Hosted usando 208
Campos 274 -280 modelos de catálogo de produtos
Módulo Braintree Python, instalando 271 , 272 criando 227 -229
Conta do sandbox Braintree, registrando, no site de administração 229
criando 270 , 271 visualizações do catálogo de produtos
https://translate.googleusercontent.com/translate_f 500/504
16/12/2021 10:52 Sem título
integrando 269 -274 edifício 230 -232
pagamentos página de detalhes do produto 347
testando 280 -283 recomendação do produto 347 -356
Arquivos PDF projeto 9
renderização 294 -297 modelo de proxy 366
enviando, por e-mail 297 -300 Pitão
Faturas em PDF Redis, usando 217 , 218
gerando 292 Python 3.8.0
PDFs, saída com Django link de download 2
link de referência 292 Código Python
Modelo de PDF tradução de código 320 -324
criando 292 , 293 tradução preguiçosa 319
permissões tradução de formas plurais 319 , 320
link de referência 455 tradução padrão 319
cache por site traduzindo 318
desativando 471 tradução variável 319
utilizando 441 , 442 Ambiente Python
instalação pip criando 3
link de referência 3 Instalador Python
pacote pip link de download 2
usado, para instalar o Django 3 , 4 Python Social Auth
Poedit link de referência 131
link de download 323
postar modelo de detalhes Q
comentários, adicionando 54 -57
PostgreSQL QuerySet
link de download 83 cerca de 21
instalando 83 , 84 avaliando 25
Pesquisa de texto completo PostgreSQL exclude (), usando 24
[ 540 ]
Página 566
https://translate.googleusercontent.com/translate_f 501/504
16/12/2021 10:52 Sem título
classificação de imagem, armazenando 220 , 222 visualizações de lista e detalhes, edifício 447 -450
instalando 215 -217 serializadores aninhados, criando 450 -452
visualizações de itens, armazenando 218 , 219 analisadores 446 , 447
usado, para armazenar visualizações de item 215 renderizadores 446 , 447
usando, com Python 217 , 218 roteadores, criando 456 , 457
Comandos Redis serializadores, definindo 445 , 446
link de referência 217 conjuntos de visualizações, criando 456 , 457
Redis, instalando no Windows 10 permissões de visualizações, adicionando 454 , 455
link de referência 216 Documentação de Rosetta
documentação redis-py link de referência 331
link de referência 217 Interface de tradução Rosetta
Redis, cenários usando 328 -331
cache 223 roteadores
contando 223 link de referência 457
últimos itens, armazenando 223 roteamento 477 , 478
pub / sub 223
filas 223
S
classificações e tabelas de classificação 223
rastreamento em tempo real 223 método save ()
expressões regulares substituindo, de ModelForm 155 -159
link de referência 29 Campo SearchVectorField
[ 541 ]
Página 567
https://translate.googleusercontent.com/translate_f 502/504
16/12/2021 10:52 Sem título
Protocolo de pesquisa
pesquisas de transferência de correio
simples 84 simples (SMTP) 43 Segurança da Camada de Transporte (TLS) 43 , 132, 511
estrutura do mapa do site semelhança trigrama
adicionando 76 -79 procurando com 90
link de referência 77 Conta do Twitter
autenticação social link de referência 140
adicionando 130 -132 tipo de conteúdo
servidor de desenvolvimento, executando através de HTTPS renderização 429 -431
132 -134
Facebook, usando 134 -140 você
Google, usando 142 -147
Namespaces de URL
link de referência 131
link de referência 29
Twitter, usando 140 -142
Padrões de URL
projeto de site social
para internacionalização 332
[ 542 ]
Página 568
V
Visualizações
adaptando, para traduções 341 -344
cache 441
formulários, manuseio 41 , 42
modelos, criando 30 -33
conjuntos de visualizações
link de referência 457
ambiente virtual (venv)
link de referência 3
C
WeasyPrint
instalando 292
link de referência 292
Interface de gateway de servidor da web (WSGI)
cerca de 5 , 471
https://translate.googleusercontent.com/translate_f 503/504
16/12/2021 10:52 Sem título
cerca de 501
link de referência 502
Cliente WebSocket
implementando 478 -484
ponderação de consultas 89
Subsistema Windows para Linux (WSL) 216
[ 543 ]
Página 569
https://translate.googleusercontent.com/translate_f 504/504