Category: frameworks

  • Django Signals: Usando sinais para comunicação desacoplada de componentes de aplicativos.

    No desenvolvimento de aplicativos web, uma das maiores vantagens que buscamos é a desacoplagem entre os componentes. Se cada parte do sistema depender diretamente de outra, você acaba com um monolito de código difícil de gerenciar, cheio de “gambiarras” que podem quebrar algo em um lugar inesperado quando você faz pequenas alterações.

    Aqui é onde os signals do Django entram em cena. Eles permitem que os componentes de um aplicativo se comuniquem sem que um saiba que o outro existe. Ou seja, os signals são a chave para manter uma comunicação interna, desacoplada e eficiente, no seu aplicativo Django.

    E, claro, fazer isso sem precisar criar uma teia de dependências entre os componentes do sistema. Porque, sejamos honestos, ninguém quer um aplicativo onde uma simples mudança em um formulário quebra a lógica de negócios inteira.

    Nesta postagem, você aprenderá o conceito de signals no Django, como eles funcionam, quando (e quando não) usá-los, e, claro, como implementá-los de maneira prática com exemplos de código.

    Mas antes de começar, vale o aviso: não use signals para tudo! Eles são úteis, mas não são o martelo que resolve todos os problemas do desenvolvimento web. E se você acha que sim, bom… talvez esteja quebrando uns parafusos por aí.

    O que são Django Signals?

    No Django, signals (ou signais) são usados para permitir que componentes de um aplicativo se comuniquem de forma desacoplada. Basicamente, você “escuta” por um evento específico e, quando esse evento ocorre, você executa uma ação correspondente.

    Imagine uma campainha de uma casa: alguém aperta o botão (dispara o evento) e a campainha toca (executa a ação).

    No Django, é algo semelhante: um sinal é emitido quando ocorre algum evento, e uma função conectada a esse sinal é executada automaticamente. Isso é útil quando você deseja que algo aconteça após a conclusão de uma tarefa específica, como salvar um objeto no banco de dados ou deletar um registro.

    Agora, por que isso é importante? Simples: desacoplamento. O componente que dispara o sinal não sabe, e não precisa saber, quem está ouvindo e respondendo ao sinal. Isso mantém seu código limpo, organizado e fácil de manter (ou pelo menos, mais fácil de entender quando você revisitar o projeto seis meses depois).

    Quando usar Django Signals 🤔

    Antes de iniciarmos nos detalhes de implementação, uma pergunta que pode estar na sua cabeça é: “Devo usar signals para tudo?” A resposta curta é não. A resposta longa é: signals são ótimos quando você precisa executar algo de forma automática em resposta a eventos que ocorrem no seu sistema, mas não quer que esses componentes fiquem acoplados diretamente.

    Aqui estão alguns exemplos de casos em que faz sentido usar Django Signals:

    • Atualizar perfis de usuários: Quando um usuário cria uma conta ou atualiza seu perfil, você pode usar um sinal para garantir que as informações relacionadas (como dados do perfil) sejam automaticamente sincronizadas.
    • Enviar notificações ou emails: Quando um novo pedido é feito, ou uma conta é criada, você pode disparar um sinal para enviar um email de boas-vindas ou notificação.
    • Auditoria e logs: Sempre que algo importante acontece (como a exclusão de um registro), você pode usar signals para registrar isso em um arquivo de log ou sistema de auditoria.

    Contudo, usar signals para tudo pode deixar seu código difícil de depurar. Eles são invisíveis até que sejam disparados, o que pode complicar um pouco as coisas quando você estiver tentando rastrear um bug. A dica é: use sinais com moderação e para eventos que realmente justifiquem a comunicação desacoplada.

    Como Django Signals Funcionam

    Os signals no Django seguem um padrão simples: você tem emissores e ouvintes. O emissor (geralmente uma ação ou evento) dispara o sinal, enquanto o ouvinte (a função que você conecta ao sinal) executa algo quando o sinal é recebido.

    Vamos direto ao exemplo básico. O Django já vem com alguns signals embutidos, como post_save e post_delete, que são disparados após um objeto ser salvo ou deletado, respectivamente. Vamos usar o post_save como exemplo para entender o funcionamento básico.

    Exemplo de uso do Django Signal com post_save

    Vamos imaginar que temos um sistema de e-commerce e queremos criar uma função que envie um email de notificação para o cliente sempre que um novo pedido for criado. Para isso, podemos usar o sinal post_save.

    Passo 1: Criando o Modelo

    Primeiro, vamos criar o modelo de Pedido:

    from django.db import models
    
    class Pedido(models.Model):
        cliente = models.CharField(max_length=100)
        produto = models.CharField(max_length=100)
        valor = models.DecimalField(max_digits=10, decimal_places=2)
        data_pedido = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return f'{self.produto} - {self.cliente}'
    Python

    Passo 2: Criando o Listener (ouvinte) do Signal

    Agora, vamos criar uma função que será executada toda vez que um novo pedido for salvo no banco de dados. Essa função vai enviar um email de confirmação para o cliente.

    from django.db.models.signals import post_save
    from django.dispatch import receiver
    from django.core.mail import send_mail
    from .models import Pedido
    
    @receiver(post_save, sender=Pedido)
    def enviar_email_confirmacao(sender, instance, created, **kwargs):
        if created:  # <- Verifica se o pedido foi criado (não apenas atualizado)
            assunto = 'Confirmação do seu pedido'
            mensagem = f'Olá {instance.cliente}, seu pedido do produto {instance.produto} foi recebido com sucesso!'
            remetente = 'no-reply@meusite.com'
            destinatario = [instance.cliente]
            
            send_mail(assunto, mensagem, remetente, destinatario)
    Python

    Passo 3: Conectando o Sinal

    A função enviar_email_confirmacao será automaticamente conectada ao sinal post_save através do decorator @receiver. O sender especifica o modelo que dispara o sinal, neste caso, o modelo Pedido. Toda vez que um novo pedido for criado, o Django dispara o sinal e nossa função envia um email ao cliente.

    Nota: No exemplo acima, usamos a função send_mail para enviar o email, mas, claro, em um sistema de produção, você provavelmente vai usar algo mais robusto como Celery para enviar emails de forma assíncrona.

    Outros Signals Úteis no Django

    O Django já inclui uma série de signals prontos para uso. Aqui estão alguns dos mais comuns:

    • pre_save: Disparado antes de um objeto ser salvo no banco de dados.
    • post_save: Disparado após um objeto ser salvo.
    • pre_delete: Disparado antes de um objeto ser deletado.
    • post_delete: Disparado após um objeto ser deletado.
    • m2m_changed: Disparado quando ocorre uma alteração em uma relação de muitos-para-muitos.

    Esses signals podem ser extremamente úteis para automatizar processos como limpeza de dados, sincronização entre modelos ou registro de auditorias.

    Exemplo: pre_delete

    Vamos dar um exemplo rápido de como usar o pre_delete. Digamos que queremos manter um registro de todos os pedidos que foram excluídos (talvez por questões de auditoria).

    Passo 1: Criando um Modelo de Auditoria

    Primeiro, criamos um modelo simples para armazenar informações sobre pedidos deletados:

    from django.db import models
    
    class PedidoDeletado(models.Model):
        cliente = models.CharField(max_length=100)
        produto = models.CharField(max_length=100)
        valor = models.DecimalField(max_digits=10, decimal_places=2)
        data_pedido = models.DateTimeField()
        data_delecao = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return f'{self.produto} excluído por {self.cliente}'
    Python

    Passo 2: Conectando o Sinal pre_delete

    Agora, vamos conectar o sinal pre_delete para salvar as informações de um pedido antes que ele seja deletado:

    from django.db.models.signals import pre_delete
    from django.dispatch import receiver
    from .models import Pedido, PedidoDeletado
    
    @receiver(pre_delete, sender=Pedido)
    def registrar_pedido_deletado(sender, instance, **kwargs):
        PedidoDeletado.objects.create(
            cliente=instance.cliente,
            produto=instance.produto,
            valor=instance.valor,
            data_pedido=instance.data_pedido
        )
    Python

    Neste exemplo, toda vez que um pedido for deletado, ele será registrado na tabela PedidoDeletado, salvando uma cópia das informações antes da exclusão.

    Criando Signals Personalizados

    Além dos signals embutidos no Django, você pode criar seus próprios signals personalizados. Isso é útil quando você precisa de uma comunicação interna muito específica entre partes do seu aplicativo.

    Exemplo de Sinal Personalizado

    Vamos imaginar um cenário onde temos um sistema de assinaturas. Queremos enviar um email de notificação quando o status de uma assinatura mudar. Para isso, podemos criar um sinal personalizado.

    Passo 1: Criando o Sinal

    No arquivo signals.py, criamos o sinal personalizado usando o Signal do Django:

    from django.dispatch import Signal
    
    # Sinal personalizado que envia o novo status da assinatura
    assinatura_alterada = Signal(providing_args=['novo_status'])
    Python

    Passo 2: Disparando o Sinal

    Agora, sempre que uma assinatura for atualizada, podemos disparar o sinal assinatura_alterada:

    from .signals import assinatura_alterada
    from .models import Assinatura
    
    def atualizar_assinatura(usuario, novo_status):
        assinatura = Assinatura.objects.get(usuario=usuario)
        assinatura.status = novo_status
        assinatura.save()
    
        # Disparando o sinal
        assinatura_alterada.send(sender=assinatura.__class__, novo_status=novo_status)
    Python

    Passo 3: Ouvindo o Sinal

    Agora, criamos um “ouvinte” para esse sinal personalizado. Quando o sinal for disparado, o ouvinte será executado e enviará uma notificação:

    from django.dispatch import receiver
    from .signals import assinatura_alterada
    
    @receiver(assinatura_alterada)
    def enviar_notificacao_alteracao(sender, novo_status, **kwargs):
        # Enviar notificação sobre a mudança de status
        print(f'O status da assinatura mudou para: {novo_status}')
    Python

    Com isso, sempre que o status de uma assinatura for alterado, o sistema vai emitir o sinal e a função de envio de notificação será acionada.

    Cuidados ao Usar Django Signals

    Os signals são poderosos, mas vêm com algumas armadilhas. Eles podem dificultar o rastreamento de bugs, especialmente quando são disparados silenciosamente em segundo plano. Além disso, como os signals não têm retorno direto (você não pode pegar a resposta de um signal disparado), depurar fluxos de trabalho que envolvem muitos signal pode ser um desafio.

    Aqui estão algumas dicas para evitar problemas com signals no Django:

    1. Mantenha-os simples: Não sobrecarregue suas funções de ouvinte com muita lógica. Isso pode tornar o código difícil de manter.
    2. Evite signals excessivos: Não use signals para processos que poderiam ser resolvidos com chamadas de funções diretas ou métodos de modelo.
    3. Sempre teste signals: Como os signals podem ser fáceis de ignorar durante o desenvolvimento, certifique-se de que você tem testes adequados para garantir que os sinais estejam funcionando corretamente.

    Conclusão

    Os signals do Django são uma ferramenta excelente para manter uma comunicação desacoplada entre os componentes do seu sistema. Eles permitem que você execute tarefas automaticamente em resposta a eventos específicos, como salvar ou deletar objetos no banco de dados.

    No entanto, como com qualquer ferramenta poderosa, eles devem ser usados com cautela. Exagerar no uso de signals pode complicar seu código e dificultar o rastreamento de bugs.

    Use-os com sabedoria, e você terá um sistema que não só funciona bem, mas também é mais fácil de manter e expandir no futuro. Afinal, comunicação interna eficiente no código é como uma boa equipe de trabalho: tudo flui melhor quando cada parte faz sua função sem precisar se intrometer nos negócios dos outros.

  • Django Rest Framework: Construindo APIs RESTful com Django.

    Django Rest Framework: Construindo APIs RESTful com Django.

    No desenvolvimento moderno de aplicações web, onde a separação entre front-end e back-end é essencial, o Django Rest Framework (DRF) se destaca como uma das principais ferramentas para criar APIs RESTful de forma rápida, segura e escalável.

    Uma API (Application Programming Interface) permite a comunicação entre sistemas diferentes, e o padrão REST (Representational State Transfer) tornou-se o mais utilizado por sua simplicidade e eficiência.

    O Django Rest Framework combina o poder do framework django com recursos avançados como autenticação, serialização e controle de permissões, tornando o desenvolvimento de APIs mais produtivo e padronizado.

    Por que REST é tão popular? Simples. Ele permite que sistemas possam se comunicar de forma eficiente e escalável, utilizando operações HTTP como GET, POST, PUT e DELETE para manipular recursos. Mas, como sempre, a teoria é uma coisa; na prática, pode ser bem mais desafiador. É aí que entra o Django REST Framework (DRF).

    O Django REST Framework é uma biblioteca poderosa que facilita a criação de APIs RESTful com Django. Ele oferece uma série de ferramentas que resolvem os problemas comuns na criação de APIs, como serialização de dados, controle de permissões, autenticação, tratamento de erros, e muito mais. E, como a maioria das bibliotecas Django, o DRF é altamente modular e extensível, o que permite que você customize quase todos os aspectos da sua API.

    Vamos direto ao ponto: nesta postagem, veremos como construir APIs RESTful utilizando o Django REST Framework, cobrindo desde a instalação e configuração básica até práticas avançadas como autenticação, filtragem, paginação, otimização e segurança…

    Instalação e Configuração do Django REST Framework

    Django Rest Framework

    Antes de qualquer coisa, você precisa ter um projeto Django em funcionamento. Se você ainda não tem um, basta criar um com os comandos:

    django-admin startproject minhaapi .
    Bash

    Agora, instale o Django REST Framework:

    pip install djangorestframework
    Bash

    Em seguida, adicione o rest_framework à lista de INSTALLED_APPS no arquivo settings.py:

    INSTALLED_APPS = [
        # Outros apps do Django...
        'rest_framework',
    ]
    Python

    Isso habilita o Django REST Framework no seu projeto.

    Configurações Iniciais

    No mesmo arquivo settings.py, podemos adicionar uma configuração básica para o DRF. Ela vai definir o comportamento padrão de permissões, autenticação e paginação da nossa API. Veja um exemplo simples:

    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.AllowAny',  # <- Permite acesso a qualquer usuário
        ],
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework.authentication.SessionAuthentication',  # <- Autenticação baseada em sessão
            'rest_framework.authentication.TokenAuthentication',    # <- Autenticação por token
        ],
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 10  # Número padrão de itens por página
    }
    Python

    Essa configuração é um ponto de partida. Definimos que a API permite qualquer requisição por enquanto (AllowAny), que ela pode usar autenticação por sessão ou por token, e que a paginação retorna 10 itens por página.

    Agora que já temos o Django REST Framework instalado e configurado, vamos criar nossa primeira API!

    Serializers: Convertendo Dados para APIs No Django Rest Framework

    Os serializers no Django REST Framework são responsáveis por converter instâncias de modelos do Django (ou outros tipos de dados complexos) em formatos simples que podem ser facilmente renderizados em JSON, ou outros formatos adequados para uma API.

    Há boatos que é possivel renderizar xml com django-rest-framework mas eu nunca vi como. Se você sabe como, ou já ouviu falar deixe um comentario aqui nesta postagem .Eu gostaria muito de saber como isso funciona.

    Imagine que temos um modelo de produto em nosso sistema Django:

    from django.db import models
    
    class Produto(models.Model):
        nome = models.CharField(max_length=100)
        descricao = models.TextField()
        preco = models.DecimalField(max_digits=10, decimal_places=2)
        data_criacao = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return self.nome
    Python

    Para expor esses dados via API, precisamos de um serializer que vai converter esse modelo para JSON e vice-versa. Veja como criar um serializer básico:

    from rest_framework import serializers
    from .models import Produto
    
    class ProdutoSerializer(serializers.ModelSerializer):
        class Meta:
            model = Produto
            fields = ['id', 'nome', 'descricao', 'preco', 'data_criacao']
    Python

    O ProdutoSerializer usa o ModelSerializer, que automaticamente mapeia os campos do modelo Produto. Isso nos poupa de definir manualmente cada campo no serializer, mas você pode fazer isso se precisar de mais controle sobre o que vai ser serializado.

    Um serializer mais manual ficaria assim:

    class ProdutoSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        nome = serializers.CharField(max_length=100)
        descricao = serializers.CharField()
        preco = serializers.DecimalField(max_digits=10, decimal_places=2)
        data_criacao = serializers.DateTimeField()
    Python

    Este exemplo dá mais controle sobre o comportamento dos campos individuais, mas na maioria dos casos, o ModelSerializer é suficiente.

    Validação de Dados

    Uma das grandes vantagens dos serializers é que eles não apenas convertem dados, mas também validam automaticamente as entradas dos usuários. O ProdutoSerializer validará o tipo de dado, comprimento dos campos, entre outras coisas.

    Se você quiser adicionar validações personalizadas, pode sobrescrever o método validate() no serializer:

    class ProdutoSerializer(serializers.ModelSerializer):
        class Meta:
            model = Produto
            fields = '__all__'
    
        def validate_preco(self, value):
            if value <= 0:
                raise serializers.ValidationError("O preço deve ser maior que zero.")
            return value
    Python

    Neste exemplo, garantimos que o preço do produto não pode ser negativo. O Django REST Framework trata a validação automaticamente e retorna mensagens de erro apropriadas.

    Viewsets: Controlando as Requisições da API No Django Rest Framework

    Os Viewsets no Django REST Framework são uma forma poderosa de lidar com as requisições da API de forma eficiente. Eles combinam as funcionalidades de várias Views tradicionais do Django em uma única classe, permitindo um código mais conciso e fácil de manter.

    Uma Viewset simplifica bastante as coisas. Ela agrupa as ações de listagem, detalhamento, criação, atualização e deleção em uma única classe.

    Aqui está um exemplo de um Viewset para nosso modelo Produto:

    from rest_framework import viewsets
    from .models import Produto
    from .serializers import ProdutoSerializer
    
    class ProdutoViewSet(viewsets.ModelViewSet):
        queryset = Produto.objects.all()
        serializer_class = ProdutoSerializer
    Python

    Com isso, o DRF automaticamente cria as rotas e métodos HTTP necessários para listar produtos, criar novos, atualizar e deletar, tudo em uma única classe. Isso economiza muito código repetitivo.

    O ModelViewSet inclui as seguintes ações por padrão:

    • list(): Retorna uma lista de objetos.
    • retrieve(): Retorna um objeto específico.
    • create(): Cria um novo objeto.
    • update(): Atualiza um objeto existente.
    • destroy(): Remove um objeto.

    Se quiser personalizar essas ações, você pode sobrescrevê-las. Por exemplo, se você quiser modificar o comportamento de listagem:

    class ProdutoViewSet(viewsets.ModelViewSet):
        queryset = Produto.objects.all()
        serializer_class = ProdutoSerializer
    
        def list(self, request):
            queryset = Produto.objects.filter(preco__gte=10)  # Exemplo: apenas produtos com preço acima de 10 dinheiros
            serializer = ProdutoSerializer(queryset, many=True)
            return Response(serializer.data)
    Python

    Isso permite adicionar lógicas específicas para cada operação.

    Routers: Mapeando URLs para as Viewsets

    Para expor nossas Viewsets via URLs, o Django REST Framework oferece o conceito de routers, que simplificam o processo de mapeamento das URLs da API.

    Aqui está como podemos adicionar um router para nossa API de produtos:

    from rest_framework.routers import DefaultRouter
    from .views import ProdutoViewSet
    
    router = DefaultRouter()
    router.register(r'produtos', ProdutoViewSet)
    
    urlpatterns = [
        path('', include(router.urls)),
    ]
    Python

    O DefaultRouter gera automaticamente todas as rotas necessárias para as operações CRUD. Ele vai criar URLs como:

    • /produtos/: Para listar e criar produtos.
    • /produtos/{id}/: Para recuperar, atualizar ou deletar um produto específico.

    Isso elimina a necessidade de mapear manualmente cada URL, tornando o código mais limpo e fácil de manter.

    Se precisar de rotas personalizadas, como uma rota para buscar produtos por nome, você pode fazer isso criando uma Action:

    from rest_framework.decorators import action
    from rest_framework.response import Response
    
    class ProdutoViewSet(viewsets.ModelViewSet):
        queryset = Produto.objects.all()
        serializer_class = ProdutoSerializer
    
        @action(detail=False, methods=['get'], url_path='buscar')
        def buscar_produto(self, request):
            nome = request.query_params.get('nome')
            produtos = Produto.objects.filter(nome__icontains=nome)
            serializer = self.get_serializer(produtos, many=True)
            return Response(serializer.data)
    Python

    Isso cria uma nova rota /produtos/buscar/?nome=play5-barato, que retorna produtos com o nome que contém o parâmetro passado.

    Permissões e Autenticação

    Uma API aberta para o público geral pode ser perigosa, especialmente quando estamos lidando com dados sensíveis. Por isso, garantir que apenas usuários autorizados possam acessar ou modificar recursos é essencial. O Django REST Framework torna esse processo muito fácil com seu sistema de permissões e autenticação.

    Permissões no Django REST Framework

    As permissões definem quem pode acessar certas views ou realizar certas ações. O Django REST Framework já traz algumas permissões básicas, que podem ser configuradas diretamente nas views ou viewsets.

    • AllowAny: Permite que qualquer pessoa acesse a view (inclusive usuários anônimos).
    • IsAuthenticated: Apenas usuários autenticados podem acessar.
    • IsAdminUser: Apenas administradores podem acessar.
    • IsAuthenticatedOrReadOnly: Usuários autenticados podem fazer tudo; usuários anônimos só podem ler.

    Podemos aplicar essas permissões diretamente nas viewsets:

    from rest_framework.permissions import IsAuthenticated
    
    class ProdutoViewSet(viewsets.ModelViewSet):
        queryset = Produto.objects.all()
        serializer_class = ProdutoSerializer
        permission_classes = [IsAuthenticated]
    Python

    Aqui, apenas usuários logados poderão acessar as rotas da API de produtos.

    Autenticação com Tokens

    A autenticação baseada em tokens é uma das formas mais comuns de proteger APIs RESTful. O DRF oferece suporte nativo para autenticação com tokens, permitindo que os usuários façam login e recebam um token único, que será enviado com cada requisição subsequente para provar sua identidade.

    Para habilitar a autenticação por token, instale o pacote djangorestframework-simplejwt:

    pip install djangorestframework-simplejwt
    Bash

    Adicione as classes de autenticação ao settings.py:

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework_simplejwt.authentication.JWTAuthentication',
        ],
    }
    Python

    Agora, configure as rotas para obter e atualizar tokens no seu urls.py:

    from rest_framework_simplejwt.views import (
        TokenObtainPairView,
        TokenRefreshView,
    )
    
    urlpatterns = [
        path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
        path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    ]
    Python

    Com isso, os usuários poderão obter um token de autenticação enviando suas credenciais (usuário e senha) para /api/token/. O token será então usado em cada requisição subsequente:

    curl -X POST http://localhost:8000/api/token/ -d "username=admin&password=12345"
    Python

    Isso retornará um token JWT, que deve ser enviado no cabeçalho Authorization em cada requisição subsequente:

    curl -H "Authorization: Bearer " http://localhost:8000/produtos/
    Python

    Filtragem, Ordenação e Paginação No Django Rest Framework

    APIs sem a capacidade de filtrar, ordenar e paginá-las podem se tornar um pesadelo de performance à medida que os dados crescem. O Django REST Framework oferece suporte nativo para esses recursos.

    Filtragem

    A filtragem permite que os clientes da API restrinjam os resultados com base em critérios específicos. Podemos usar o pacote django-filter para facilitar essa tarefa:

    pip install django-filter
    Bash

    Em settings.py, adicione a configuração para habilitar o django-filter no DRF:

    REST_FRAMEWORK = {
        'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    }
    Python

    Agora, no views.py, podemos adicionar filtragem a nossa Viewset de produtos:

    from django_filters.rest_framework import DjangoFilterBackend
    
    class ProdutoViewSet(viewsets.ModelViewSet):
        queryset = Produto.objects.all()
        serializer_class = ProdutoSerializer
        filter_backends = [DjangoFilterBackend]
        filterset_fields = ['nome', 'preco']
    Python

    Com isso, podemos filtrar produtos por nome e preco diretamente na URL:

    http://localhost:8000/produtos/?nome=Mouse&preco=50
    Python

    Ordenação

    Além de filtrar, podemos ordenar os resultados da API. Para isso, usamos a classe OrderingFilter:

    from rest_framework.filters import OrderingFilter
    
    class ProdutoViewSet(viewsets.ModelViewSet):
        queryset = Produto.objects.all()
        serializer_class = ProdutoSerializer
        filter_backends = [DjangoFilterBackend, OrderingFilter]
        filterset_fields = ['nome', 'preco']
        ordering_fields = ['preco', 'data_criacao']
    Python

    Agora, podemos ordenar os produtos por preco ou data_criacao:

    http://localhost:8000/produtos/?ordering=preco
    http://localhost:8000/produtos/?ordering=-preco
    Python

    Paginação

    Paginar os resultados é crucial para APIs que podem retornar grandes volumes de dados. No Django REST Framework, a paginação pode ser configurada globalmente no settings.py ou individualmente em cada Viewset.

    Aqui está a configuração global para usar paginação baseada em números de página:

    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 10
    }
    Python

    Com essa configuração, a resposta da API será automaticamente paginada em blocos de 10 itens por vez. Para navegar pelas páginas, basta adicionar o parâmetro page à URL:

    http://localhost:8000/produtos/?page=2
    Python

    Testando APIs RESTful

    Uma das melhores práticas no desenvolvimento de software é testar suas APIs. Testes garantem que seu código funcione conforme esperado e que futuras alterações não quebrem nada.

    O Django REST Framework oferece o APIClient, uma ferramenta que facilita a escrita de testes para APIs. Vamos ver como testar nossa API de produtos.

    from rest_framework.test import APITestCase
    from django.urls import reverse
    from rest_framework import status
    from .models import Produto
    
    class ProdutoAPITest(APITestCase):
        
        def setUp(self):
            self.produto = Produto.objects.create(
                nome="Teclado",
                descricao="Teclado mecânico",
                preco=150.00
            )
        
        def test_listar_produtos(self):
            url = reverse('produto-list')
            response = self.client.get(url)
            self.assertEqual(response.status_code, status.HTTP_200_OK)
        
        def test_criar_produto(self):
            url = reverse('produto-list')
            data = {'nome': 'Mouse', 'descricao': 'Mouse óptico', 'preco': 50.00}
            response = self.client.post(url, data, format='json')
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
    Python

    No teste acima, usamos o APITestCase para testar duas coisas: listar os produtos e criar um novo produto. Isso garante que nossas operações básicas da API estão funcionando como esperado.

    https://wordpress.vps9144.panel.icontainer.cloud/testes-unitarios-com-django/
    Só Para lembrar: Caso precise se aprofundar mais nos testes
    unitarios da um pulo na postagem acima ;D

    Melhorando Performance com Viewsets Otimizados

    Performance é sempre uma preocupação, especialmente quando se trata de APIs que podem ser acessadas por múltiplos clientes simultaneamente. O Django REST Framework permite otimizar as consultas ao banco de dados usando os métodos select_related() e prefetch_related().

    Esses métodos são úteis quando estamos lidando com relações entre modelos, como ForeignKey e ManyToManyField.

    Imagine que cada produto tenha um categoria, que é uma ForeignKey. Para evitar múltiplas consultas ao banco, podemos usar select_related:

    class ProdutoViewSet(viewsets.ModelViewSet):
        queryset = Produto.objects.select_related('categoria').all()
        serializer_class = ProdutoSerializer
    Python

    O select_related() carrega a relação ForeignKey em uma única consulta, reduzindo a sobrecarga no banco de dados.

    Evitando o Problema N+1 no Django Rest Framework

    Um dos problemas mais comuns em APIs mal otimizadas é o N+1 problem. Isso acontece quando a aplicação faz uma consulta principal (a primeira) e depois faz N outras consultas para obter dados relacionados, o que é extremamente ineficiente.

    Por exemplo, se tivermos uma lista de produtos, e cada produto tiver uma relação com uma categoria, sem select_related(), o Django faria uma consulta para listar os produtos e, depois, N consultas para cada uma das categorias associadas. O uso de select_related() evita isso.

    Tratamento de Erros e Exceções

    Ninguém gosta de ver erros sem sentido ao usar uma API. O Django REST Framework facilita o tratamento de exceções e erros para garantir que as respostas da API sejam claras e informativas.

    Por padrão, o DRF já lida com muitos erros comuns (como 404 Not Found ou 400 Bad Request). No entanto, você pode personalizar o comportamento criando suas próprias handlers de erro.

    Aqui está um exemplo de como capturar exceções e retornar uma resposta customizada:

    from rest_framework.views import exception_handler
    
    def custom_exception_handler(exc, context):
        response = exception_handler(exc, context)
        
        if response is not None:
            response.data['status_code'] = response.status_code
        
        return response
    Python

    Em settings.py, adicione a referência para esse handler:

    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'meuprojeto.utils.custom_exception_handler', 
    }
    Python

    Agora, todas as exceções serão tratadas pelo seu handler customizado, onde você pode adicionar informações extras à resposta, como status code, mensagens mais amigáveis, etc.

    Implementando Versionamento de API No Django Rest Framework

    Versionar uma API é crucial quando há a necessidade de fazer atualizações sem quebrar a compatibilidade com clientes antigos. O Django REST Framework facilita a implementação de versionamento, permitindo que você diferencie as versões da API nas URLs ou nos cabeçalhos.

    Versionamento nas URLs

    A abordagem mais comum é incluir a versão na URL. Aqui está um exemplo de como fazer isso:

    from rest_framework.versioning import URLPathVersioning
    
    REST_FRAMEWORK = {
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    }
    
    urlpatterns = [
        path('api/v1/produtos/', ProdutoViewSet.as_view(), name='produtos-v1'),
        path('api/v2/produtos/', ProdutoViewSetV2.as_view(), name='produtos-v2'),
    ]
    Python

    Agora você tem duas versões da API rodando ao mesmo tempo. A versão v2 pode conter mudanças significativas sem afetar os clientes que ainda estão usando a v1.

    Documentação Automática com Swagger

    Uma boa documentação é essencial para qualquer API. O Django REST Framework pode gerar automaticamente uma documentação interativa para sua API usando ferramentas como o drf-yasg ou o Swagger.

    Instale o pacote:

    pip install drf-yasg
    Python

    Em seguida, adicione a configuração em urls.py:

    from rest_framework import permissions
    from drf_yasg.views import get_schema_view
    from drf_yasg import openapi
    
    schema_view = get_schema_view(
        openapi.Info(
            title="Minha API",
            default_version='v1',
            description="Documentação da API",
        ),
        public=True,
        permission_classes=(permissions.AllowAny,),
    )
    
    urlpatterns = [
        path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    ]
    Python

    Agora você pode acessar a documentação completa da sua API em /swagger/. A interface gerada pelo Swagger permite que os desenvolvedores vejam todas as rotas disponíveis, além de testar as requisições diretamente na página.

    Conclusão

    Neste artigo, detalhamos como construir APIs RESTful usando o Django REST Framework, passando desde a instalação e configuração até conceitos mais avançados, como autenticação por token, filtragem, paginação e otimização de consultas.

    Construir APIs pode parecer desafiador no início, mas o Django REST Framework oferece ferramentas maravilhosas para facilitar o desenvolvimento, mantendo um alto nível de flexibilidade e extensibilidade. Além disso, com boas práticas de segurança e documentação, a API que você criar estará pronta para ser consumida de forma eficiente e segura.

    Seguir as dicas e exemplos mostrados aqui, você terá uma API RESTful pronta para escalar.

    Se você chegou até aqui, parabéns! Agora é hora de colocar a mão na massa e construir suas próprias APIs.

  • Sistema de Autenticação Django: Implementando Autenticação e Autorização

    Sistema de Autenticação Django: Implementando Autenticação e Autorização

    A autenticação e autorização em uma aplicação web são os primeiros mecanismos de defesa contra acessos não autorizados. Quando implementamos autenticação, estamos garantindo que os usuários possam entrar no sistema de forma segura, fornecendo suas credenciais. A autorização, por outro lado, assegura que esses usuários só possam acessar as funcionalidades e dados que estão permitidos a eles.

    Com o Django, um dos frameworks web mais populares em Python, temos uma série de ferramentas para criar um sistema confiavel de autenticação e autorização sem muita dor de cabeça. No entanto, para aplicações mais complexas, a necessidade de customizações e extensões do sistema padrão pode surgir.

    Neste artigo, vamos abordar como implementar um sistema de autenticação do zero, customizar o modelo de usuário, controlar permissões e grupos, além de integrar o Django Allauth para facilitar logins com redes sociais. Tudo isso sem perder de vista as melhores práticas de segurança.

    O sistema nativo do Django: Por onde começar?

    O Django já vem com um sistema de autenticação pré-configurado e integrado ao seu framework. Isso significa que, assim que você criar um projeto Django, o sistema de autenticação básico já está disponível.

    O que está disponível no pacote padrão?

    1. Modelo User: O modelo de usuário padrão é gerenciado por django.contrib.auth. Ele armazena informações como username, senha (de forma segura, utilizando hashing), email, primeiro nome, último nome, e permissões associadas ao usuário.
    2. Sessões e Cookies: O Django usa cookies de sessão para manter os usuários logados após o login, de forma que você não precisa implementar isso manualmente.
    3. Autenticação básica: A função authenticate() e login() estão prontas para serem utilizadas, permitindo que você autentique um usuário e o conecte à sessão.

    Essas funcionalidades cobrem a maior parte dos casos básicos, mas você provavelmente precisará configurar algumas coisas manualmente, como já mencionamos no início com o LOGIN_URL, LOGOUT_URL e LOGIN_REDIRECT_URL.

    Agora que você entende o básico, vamos avançar para configurações mais detalhadas.

    Implementando um sistema de Login e Logout no Django

    Detalhando a View de Login

    Na seção anterior, mostramos uma view básica de login. No entanto, podemos detalhar um pouco mais para incluir melhores práticas, como validação de formulários e tratamento de erros:

    from django.contrib.auth import authenticate, login
    from django.shortcuts import render, redirect
    from django import forms
    from django.contrib.auth.forms import AuthenticationForm
    
    def login_view(request):
        if request.method == 'POST':
            form = AuthenticationForm(request, data=request.POST)
            if form.is_valid():
                username = form.cleaned_data.get('username')
                password = form.cleaned_data.get('password')
                user = authenticate(request, username=username, password=password)
                if user is not None:
                    login(request, user)
                    return redirect('home')
                else:
                    return render(request, 'login.html', {'form': form, 'error': 'Usuário ou senha inválidos.'})
            else:
                return render(request, 'login.html', {'form': form, 'error': 'Dados de login inválidos.'})
        else:
            form = AuthenticationForm()
        return render(request, 'login.html', {'form': form})
    Python

    Aqui, usamos o formulário AuthenticationForm, uma classe já fornecida pelo Django, que realiza as verificações de forma mais robusta e centralizada.

    View de Logout

    O logout, por outro lado, é bem simples e não precisa de muita personalização. Você pode usar a view genérica LogoutView do Django, que já faz todo o trabalho para você:

    from django.contrib.auth.views import LogoutView
    
    urlpatterns = [
        path('logout/', LogoutView.as_view(), name='logout'),
    ]
    Python

    Esse logout invalida a sessão atual do usuário e redireciona para a página que você configurou no LOGOUT_REDIRECT_URL.

    Criando as Templates

    Para exibir os formulários de login e logout, você precisará criar templates. Um exemplo básico de template para login (login.html) poderia ser:

    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Loginbutton>
    form>
    
    {% if error %}
        <p style="color: red;">{{ error }}p>
    {% endif %}
    Python

    Esse template renderiza o formulário que criamos na view e, se houver algum erro (usuário ou senha inválidos), ele será exibido de forma amigável ao usuário.

    https://wordpress.vps9144.panel.icontainer.cloud/introducao-ao-framework-django-compreendendo-o-framework-e-sua-arquitetura/#comments

    Autorização no Django: Grupos e Permissões

    Agora que configuramos a parte de autenticação, vamos focar na autorização. O Django também vem com um sistema poderoso de permissões baseado em usuários e grupos.

    Como funcionam as permissões no Django?

    O Django aplica permissões a três níveis: add, change e delete. Para cada modelo registrado na sua aplicação, ele cria automaticamente essas três permissões. Além disso, você pode definir permissões customizadas nos seus modelos.

    Vamos criar um exemplo onde apenas usuários com a permissão can_view_dashboard podem acessar uma view de dashboard.

    Definindo permissões customizadas

    No modelo onde você deseja aplicar a permissão, basta definir as permissões personalizadas usando a classe Meta:

    from django.db import models
    
    class Report(models.Model):
        name = models.CharField(max_length=100)
        date_created = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            permissions = [
                ('can_view_dashboard', 'Pode ver o painel'),
            ]
    Python

    Agora, para aplicar essa permissão na view, usamos o decorator @permission_required:

    from django.contrib.auth.decorators import permission_required
    
    @permission_required('yourapp.can_view_dashboard')
    def dashboard_view(request):
        return render(request, 'dashboard.html')
    Python

    Se o usuário não tiver a permissão can_view_dashboard, ele será automaticamente redirecionado para a página de login ou uma página de erro.

    Trabalhando com Grupos

    Uma maneira mais eficiente de gerenciar permissões, especialmente em aplicações com muitos usuários, é usar Grupos. Você pode adicionar permissões a um grupo e, em seguida, atribuir usuários a esse grupo. Isso evita a necessidade de configurar permissões individualmente para cada usuário.

    Aqui está como você pode criar grupos e associar permissões a eles no Django Admin:

    1. Acesse o Django Admin.
    2. No painel, clique em “Groups”.
    3. Crie um novo grupo e adicione as permissões desejadas a ele.
    4. Agora, ao editar um usuário, você pode simplesmente associá-lo a um grupo.

    Esse sistema é muito útil para papéis de usuários como “Admin”, “Editor”, “Moderador”, entre outros.

    Customizando o Sistema de Usuários

    No início, mostramos como estender o modelo User padrão para adicionar novos campos. Vamos aprofundar um pouco mais, explicando quando faz sentido usar AbstractUser e quando você deveria considerar AbstractBaseUser.

    Usando AbstractBaseUser para Controle Completo

    Se você precisar de controle total sobre o modelo de usuários (talvez você não queira usar nomes de usuários, apenas emails, por exemplo), AbstractBaseUser é a escolha ideal. Ele fornece o esqueleto do sistema de autenticação sem nenhum campo adicional, e você precisa definir os campos que deseja.

    Aqui está um exemplo básico de como criar um modelo de usuário totalmente customizado:

    from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
    from django.db import models
    
    class CustomUserManager(BaseUserManager):
        def create_user(self, email, password=None, **extra_fields):
            if not email:
                raise ValueError('O email deve ser fornecido')
            email = self.normalize_email(email)
            user = self.model(email=email, **extra_fields)
            user.set_password(password)
            user.save(using=self._db)
            return user
    
        def create_superuser(self, email, password=None, **extra_fields):
            extra_fields.setdefault('is_staff', True)
            extra_fields.setdefault('is_superuser', True)
    
            return self.create_user(email, password, **extra_fields)
    
    class CustomUser(AbstractBaseUser):
        email = models.EmailField(unique=True)
        first_name = models.CharField(max_length=30)
        last_name = models.CharField(max_length=30)
        is_active = models.BooleanField(default=True)
        is_staff = models.BooleanField(default=False)
        
        objects = CustomUserManager()
    
        USERNAME_FIELD = 'email'
        REQUIRED_FIELDS = ['first_name', 'last_name']
    Python

    Neste exemplo, a autenticação é baseada no email, e não em usernames. Você também define o gerenciador de usuários customizado para criar usuários e superusuários corretamente.

    Sistema de Autenticação com Django Allauth

    Para aplicações modernas, oferecer aos usuários opções de login via redes sociais pode ser crucial. Isso facilita a entrada de novos usuários sem que eles precisem passar pelo processo de criação de contas manuais. Para facilitar isso, usamos o Django Allauth.

    O Allauth é um pacote que oferece suporte a várias formas de autenticação, incluindo login via redes sociais, registro de novos usuários, verificação de email, recuperação de senha, entre outros. Além disso, ele já vem com templates prontos para todas essas funcionalidades, o que economiza um bom tempo de desenvolvimento.

    Instalação e Configuração

    Vamos começar instalando o pacote:

    pip install django-allauth
    Python

    Agora, você precisa adicionar o Allauth ao seu arquivo settings.py. Isso envolve adicionar alguns apps ao seu projeto, além de configurar os backends de autenticação:

    INSTALLED_APPS = [
        # Apps padrão do Django
        'django.contrib.sites',  # Necessário para o Django Allauth
    
        # Apps do Django Allauth
        'allauth',
        'allauth.account',
        'allauth.socialaccount',
        'allauth.socialaccount.providers.google',  # Exemplificando a integração com o Google
    ]
    
    # Configuração para o Django Sites Framework (requisito do Allauth)
    SITE_ID = 1
    
    # Definindo o backend de autenticação para o Allauth
    AUTHENTICATION_BACKENDS = [
        'django.contrib.auth.backends.ModelBackend',
        'allauth.account.auth_backends.AuthenticationBackend',
    ]
    
    # URLs de redirecionamento
    LOGIN_REDIRECT_URL = '/'
    ACCOUNT_LOGOUT_REDIRECT_URL = '/'
    
    # Configurações extras para o Allauth
    ACCOUNT_EMAIL_VERIFICATION = 'mandatory'  # Verificação de email obrigatória
    ACCOUNT_EMAIL_REQUIRED = True
    Python

    Aqui estamos adicionando os apps allauth e allauth.socialaccount ao nosso projeto. Também incluímos o provedor social do Google como exemplo. O Allauth oferece suporte a diversos provedores (Facebook, GitHub, Twitter, entre outros), e a configuração para cada um deles é bem similar.

    Configurando as URLs

    O próximo passo é adicionar as URLs do Allauth ao seu arquivo urls.py:

    from django.urls import path, include
    
    urlpatterns = [
        # Suas outras rotas
        path('accounts/', include('allauth.urls')),
    ]
    Python

    O Allauth já traz um conjunto completo de URLs para login, logout, cadastro, recuperação de senha, verificação de email e login social. Tudo isso será incluído automaticamente ao adicionar essa linha.

    Configurando o Login Social (Exemplo com Google)

    Para permitir o login com Google, você precisa configurar as credenciais da sua aplicação na console do Google Developers. Depois de criar um novo projeto e ativar o Google OAuth, você receberá um Client ID e um Client Secret. Essas informações devem ser adicionadas ao seu projeto Django.

    No admin do Django, vá até Social applications (Aplicações Sociais), adicione uma nova aplicação, selecione “Google” como provedor e adicione o Client ID e Secret. Você também deve configurar a URL de redirecionamento, que será algo como:

    http://localhost:8000/accounts/google/login/callback/
    Python

    Testando o Login Social

    Com as credenciais configuradas, o login via Google estará disponível na página de login (/accounts/login/). O Allauth gera automaticamente os botões de login social baseados nos provedores que você configurou.

    A partir deste ponto, os usuários poderão se registrar e fazer login utilizando suas contas Google. O Allauth cuidará de toda a parte de autenticação e criação de usuários, incluindo o armazenamento seguro de tokens de acesso e atualizações de perfil.

    Personalizando o Django Allauth

    Se você quiser personalizar o comportamento padrão do Allauth (por exemplo, mudar as mensagens de erro, os templates ou o fluxo de autenticação), isso também é bastante fácil. O Allauth oferece templates personalizáveis que você pode sobrescrever. Crie um diretório chamado templates/account no seu projeto e adicione templates personalizados para views de login, cadastro e email.

    Por exemplo, um template customizado para login (login.html) pode ser algo assim:

    {% extends "base.html" %}
    {% block content %}
        <h2>Faça login na sua contah2>
        <form method="post" action="{% url 'account_login' %}">
            {% csrf_token %}
            {{ form.as_p }}
            <button type="submit">Loginbutton>
        form>
        <p>Ou faça login com:p>
        <a href="{% provider_login_url 'google' %}">Login com Googlea>
    {% endblock %}
    Python

    Esse template oferece uma experiência de login tradicional, com a opção de login social integrada.

    Segurança: Melhorando a Proteção do Sistema de Autenticação

    Um sistema de autenticação robusto precisa ser seguro. O Django já oferece uma série de boas práticas de segurança por padrão, mas algumas medidas adicionais são altamente recomendadas para proteger ainda mais sua aplicação.

    Uso de HTTPS

    Certifique-se de que sua aplicação esteja usando HTTPS em produção. Isso garante que os dados transmitidos entre o cliente e o servidor sejam criptografados, o que é crucial para impedir a interceptação de credenciais de login. No settings.py, você pode habilitar o uso de HTTPS com as seguintes configurações:

    # Redireciona todas as requisições HTTP para HTTPS
    SECURE_SSL_REDIRECT = True
    
    # Garante que o cookie de sessão só será enviado por HTTPS
    SESSION_COOKIE_SECURE = True
    
    # Garante que o cookie CSRF só será enviado por HTTPS
    CSRF_COOKIE_SECURE = True
    Python

    Bloqueio de Tentativas de Login Mal-Sucedidas

    Para impedir ataques de força bruta, onde um invasor tenta várias combinações de nome de usuário e senha até acertar, você pode limitar o número de tentativas de login que um usuário pode fazer antes de ser temporariamente bloqueado.

    Um pacote popular para isso é o django-axes:

    pip install django-axes
    Python

    Adicione o django-axes ao seu settings.py:

    INSTALLED_APPS = [
        # Apps padrão
        'axes',
    ]
    
    MIDDLEWARE = [
        'axes.middleware.AxesMiddleware',
        # Outros middlewares
    ]
    
    # Configuração do django-axes
    AXES_FAILURE_LIMIT = 5  # Número de tentativas permitidas antes de bloquear
    AXES_COOLOFF_TIME = timedelta(minutes=15)  # Tempo de bloqueio após o limite ser atingido
    Python

    Com isso configurado, o django-axes rastreará as tentativas de login e bloqueará automaticamente endereços IP que ultrapassarem o limite de tentativas falhas.

    Verificação de Email

    Habilitar a verificação de email é uma maneira importante de garantir que os usuários forneçam endereços de email válidos. O Django Allauth já possui suporte para verificação de email integrada. No settings.py, você pode forçar a verificação de email:

    ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
    ACCOUNT_EMAIL_REQUIRED = True
    Python

    Isso garantirá que o usuário só poderá acessar o sistema após confirmar seu endereço de email.

    Testando o Sistema de Autenticação

    Depois de implementar toda essa estrutura, é essencial testar o sistema de autenticação e autorização. O Django facilita os testes com suas ferramentas de TestCase.

    Um teste básico para verificar o login pode ser algo assim:

    from django.test import TestCase
    from django.contrib.auth import get_user_model
    
    class LoginTest(TestCase):
        def setUp(self):
            self.user = get_user_model().objects.create_user(username='testuser', password='testpassword')
    
        def test_login(self):
            response = self.client.post('/login/', {'username': 'testuser', 'password': 'testpassword'})
            self.assertEqual(response.status_code, 302)  # Redirecionamento após o login
    Python

    Esse teste cria um usuário e verifica se ele consegue fazer login com sucesso. Testar todas as partes do sistema é crucial para garantir que ele funcione corretamente em produção.

    Conclusão

    A autenticação e autorização são peças fundamentais para qualquer aplicação web. O Django, com seu sistema nativo, já oferece uma estrutura robusta e segura, que pode ser facilmente estendida e adaptada para necessidades específicas.

    Quando integramos ferramentas como o Django Allauth, adicionamos mais conveniência e flexibilidade, permitindo que os usuários façam login com contas sociais e criando um sistema de autenticação altamente customizável.

    Além disso, medidas de segurança, como o uso de HTTPS, bloqueio de tentativas de login e verificação de email, são fundamentais para proteger os dados dos usuários e garantir que seu sistema não seja alvo fácil para ataques.

    Espero que este passoo-a-passo tenha coberto de forma abrangente os principais aspectos da autenticação e autorização no Django. Agora, é só implementar e seguir as melhores práticas de segurança!

  • Modelos Avançados e Gerenciamento de Dados no Django

    Modelos Avançados e Gerenciamento de Dados no Django

    Quando trabalhamos com Django, uma das primeiras grandes “revelações” que temos é que os modelos são o coração do gerenciamento de dados. Se você nunca precisou lidar diretamente com SQL (e parabéns por isso!), é provável que ainda assim tenha criado e manipulado um modelo em Django sem sequer pensar duas vezes. É aqui que o “milagre” acontece: o ORM do Django traduz as suas definições de modelos Python em comandos SQL, permitindo que você trate os dados como objetos de forma quase mágica. Quase.

    Entretanto, à medida que sua aplicação cresce e suas necessidades de dados se tornam mais complexas, esse simples CRUD (Create, Read, Update, Delete) começa a parecer insuficiente. Modelos interligados por relacionamentos, consultas complexas, otimização de acesso a dados e gerenciamento de migrações se tornam desafios que não dá mais para evitar.

    Neste artigo, vamos dar uma olhada como trabalhar de forma avançada com modelos Django, cobrindo desde relacionamentos entre modelos até a criação de gerenciadores personalizados para consultas eficientes e o gerenciamento de migrações de banco de dados. Se você ainda acha que criar modelos é só adicionar models.Model e pronto, vamos além disso. E se você já teve medo de corromper seu banco de dados com uma migração malfeita, respire fundo e continue lendo.

    Trabalhando com relacionamentos entre modelos

    No Django, modelos não vivem isolados. Eles frequentemente se relacionam com outros modelos, criando uma rede de interdependências que espelha a estrutura de dados do seu aplicativo. Aqui, entra o famoso conceito dos relacionamentos entre tabelas, como OneToOne, ForeignKey, e ManyToMany. Cada um desses tipos de relacionamento tem um papel crucial, e é importante saber qual escolher para cada situação.

    Relacionamento OneToOne

    O relacionamento OneToOne é, como o nome sugere, uma conexão direta de um para um entre duas tabelas. É muito usado em casos onde você precisa expandir um modelo já existente sem modificar a tabela original. Um exemplo clássico é quando você precisa adicionar dados extras a um usuário, mas sem mexer na tabela User padrão do Django.

    from django.contrib.auth.models import User
    from django.db import models
    
    class Perfil(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
        bio = models.TextField()
        website = models.URLField()
    
        def __str__(self):
            return self.user.username
    Python

    Nesse exemplo, o modelo Perfil estende o modelo User. Cada instância de Perfil está ligada a exatamente um usuário, e vice-versa. Se o usuário for deletado, o perfil correspondente também será removido, graças ao parâmetro on_delete=models.CASCADE.

    Quando usar OneToOne:

    • Quando cada item de um modelo deve ter exatamente um correspondente em outro modelo.
    • Casos em que você precisa adicionar informações extras a uma tabela sem alterá-la diretamente (como estender o modelo User do Django).

    Relacionamento ForeignKey

    O relacionamento ForeignKey é o mais comum e cria uma relação de muitos para um. Ou seja, vários registros em uma tabela podem estar relacionados a um único registro em outra. Vamos ver um exemplo simples usando um blog.

    class Autor(models.Model):
        nome = models.CharField(max_length=100)
        email = models.EmailField()
    
        def __str__(self):
            return self.nome
    
    class Postagem(models.Model):
        titulo = models.CharField(max_length=200)
        conteudo = models.TextField()
        autor = models.ForeignKey(Autor, on_delete=models.CASCADE)
    
        def __str__(self):
            return self.titulo
    Python

    Aqui, cada Postagem está associada a um único Autor, mas um Autor pode ter várias Postagens. Novamente, o parâmetro on_delete=models.CASCADE garante que, se um autor for excluído, todas as suas postagens também sejam removidas.

    Quando usar ForeignKey:

    • Sempre que você tiver uma relação de muitos para um, como postagens de blog e autores, comentários e postagens, pedidos e clientes, etc.
    • Para garantir integridade referencial (opções como CASCADE e SET_NULL podem ser úteis, dependendo da necessidade de deletar ou manter registros órfãos).

    Relacionamento ManyToMany

    O relacionamento ManyToMany é usado quando ambos os lados de um relacionamento podem ter múltiplas ocorrências. Um exemplo clássico é o relacionamento entre alunos e cursos. Um aluno pode estar matriculado em vários cursos, e um curso pode ter muitos alunos.

    class Curso(models.Model):
        nome = models.CharField(max_length=200)
        descricao = models.TextField()
    
        def __str__(self):
            return self.nome
    
    class Aluno(models.Model):
        nome = models.CharField(max_length=100)
        cursos = models.ManyToManyField(Curso)
    
        def __str__(self):
            return self.nome
    Python

    Aqui, um Aluno pode estar matriculado em vários cursos, e um Curso pode ter muitos alunos. Django, nos bastidores, cria uma tabela intermediária para gerenciar esse relacionamento, poupando você de ter que definir manualmente essa estrutura.

    Qual melhor momento para usar ManyToMany:

    • Quando ambos os lados do relacionamento podem ter múltiplos vínculos, como alunos e cursos, tags e postagens, produtos e categorias, etc.
    • Ou quando você deseja que o Django crie e gerencie automaticamente as tabelas intermediárias.

    Gerenciadores personalizados em Django

    Em projetos maiores, onde as consultas de banco de dados se tornam mais complexas, os gerenciadores personalizados se tornam uma ferramenta essencial para manter o código limpo e reutilizável. O gerenciador padrão do Django é o objects, mas às vezes você precisa de algo mais específico, como consultar apenas registros ativos, criar métodos que façam agregações, ou até otimizar a quantidade de queries SQL geradas.

    O que são gerenciadores personalizados?

    Gerenciadores personalizados são classes que permitem modificar ou estender o comportamento padrão das consultas SQL. Por exemplo, se você tiver um modelo que contém um campo booleano ativo, e você só quiser listar os objetos onde ativo=True, você pode criar um gerenciador que automatize essa consulta.

    class AtivoManager(models.Manager):
        def get_queryset(self):
            return super().get_queryset().filter(ativo=True)
    
    class Produto(models.Model):
        nome = models.CharField(max_length=100)
        preco = models.DecimalField(max_digits=10, decimal_places=2)
        ativo = models.BooleanField(default=True)
    
        objects = models.Manager()  # <-- Gerenciador padrão
        ativos = AtivoManager()  # <-- Gerenciador personalizado
    
        def __str__(self):
            return self.nome
    Python

    Aqui, temos dois gerenciadores: o objects, que retorna todos os produtos, e o ativos, que retorna apenas os produtos onde ativo=True. Usar o ativos facilita consultas sem precisar reescrever filtros repetidamente.

    # Todos os produtos
    produtos = Produto.objects.all()
    
    # Apenas produtos ativos
    produtos_ativos = Produto.ativos.all()
    Python

    Quando usar gerenciadores personalizados?

    Com certeza você deve ter se perguntado isso 😅

    Gerenciadores personalizados são úteis quando você tem filtros ou lógicas comuns que precisam ser aplicados consistentemente em todo o seu projeto. Eles ajudam a centralizar essa lógica, em vez de espalhar condições repetitivas pelo código.

    • Filtragem automática de registros: Como visto no exemplo anterior, para gerenciar estados como “ativos” ou “inativos”.
    • Consultas complexas: Quando você precisa aplicar agregações, juntar tabelas ou realizar operações mais sofisticadas.
    • Otimização de consultas: Evitar consultas repetitivas ou ineficientes, utilizando prefetching ou seleções específicas de campos.

    Migrações de banco de dados

    Nenhum projeto Django está completo sem lidar com migrações de banco de dados. Afinal, à medida que seu projeto evolui, seus modelos mudam — e, consequentemente, suas tabelas no banco de dados também precisam mudar. As migrações do Django facilitam essa transição, permitindo que você sincronize seu banco de dados com suas mudanças de código.

    O que são migrações?

    Migrações são arquivos gerados pelo Django que contêm instruções para o banco de dados sobre como criar, modificar ou deletar tabelas e colunas. Sempre que você altera um modelo (adiciona um campo, remove um relacionamento, etc.), você precisa gerar uma migração correspondente que “comunica” ao banco de dados como ele deve se ajustar a essas mudanças.

    Por exemplo, se você adicionar um novo campo descricao ao modelo Produto, você precisará criar uma migração correspondente:

    python manage.py makemigrations
    Python

    Esse comando analisa as mudanças nos modelos e cria um arquivo de migração, que pode ser aplicado ao banco de dados com:

    python manage.py migrate
    Python

    Migrações automáticas vs. manuais

    Em muitos casos, o Django é capaz de gerar migrações automaticamente com base nas suas mudanças de modelo. No entanto, há situações em que as migrações automáticas podem não ser suficientes ou podem gerar comportamentos inesperados. Nesses casos, você pode editar manualmente os arquivos de migração para garantir que a transição ocorra conforme o esperado.

    Exemplo de uma migração automática para adicionar um campo:

    class Migration(migrations.Migration):
    
        dependencies = [
            ('app_name', '0001_initial'),
        ]
    
        operations = [
            migrations.AddField(
                model_name='produto',
                name='descricao',
                field=models.TextField(default=''),
            ),
        ]
    Python

    Problemas comuns em migrações

    Embora o sistema de migrações do Django seja bastante robusto, alguns problemas podem surgir, como conflitos de migração (quando duas ou mais migrações tentam modificar a mesma coisa) ou erros ao tentar aplicar migrações a tabelas que já contêm dados.

    Dicas para lidar com problemas comuns:

    • Revertendo migrações: Se algo der errado, você pode reverter uma migração com python manage.py migrate app_name .
    • Migrações squashed: Em projetos grandes com muitas migrações, você pode “esmagar” várias migrações antigas em uma única migração para melhorar a eficiência.

    Práticas recomendadas para gerenciamento de banco de dados em Django

    Gerenciar um banco de dados eficientemente é essencial para garantir o bom desempenho da sua aplicação Django, especialmente à medida que ela cresce e passa a lidar com grandes volumes de dados. Abaixo, apresentamos algumas práticas recomendadas que você pode aplicar para otimizar consultas, manter a integridade dos dados e garantir que o seu banco de dados continue escalável à medida que sua aplicação evolui.

    Índices de banco de dados

    Uma das maneiras mais eficientes de melhorar a performance de consultas frequentes é usar índices no banco de dados. Um índice funciona como um índice de um livro, facilitando a busca de dados específicos sem a necessidade de varrer toda a tabela.

    No Django, você pode adicionar índices usando o parâmetro db_index=True nos campos:

    class Produto(models.Model):
        nome = models.CharField(max_length=100, db_index=True)
        preco = models.DecimalField(max_digits=10, decimal_places=2)
    Python

    Isso é especialmente útil para campos que são frequentemente filtrados ou ordenados, como IDs, datas, ou campos de busca. No entanto, deve-se usar índices com moderação, pois, embora acelerem a leitura, podem impactar a performance de escrita, já que o banco precisa atualizar os índices sempre que um registro é alterado ou inserido.

    Evitar o problema N+1

    O problema N+1 acontece quando o Django faz consultas separadas para cada item em uma relação ao invés de fazer uma única consulta otimizada. Isso pode ocorrer com ForeignKey ou ManyToMany, gerando um número excessivo de consultas ao banco de dados.

    Para evitar isso, é importante usar os métodos select_related e prefetch_related. O select_related faz um join SQL e traz os dados relacionados em uma única consulta. Já o prefetch_related carrega os dados relacionados em consultas separadas, mas de maneira eficiente, lidando bem com relacionamentos ManyToMany.

    Exemplo com select_related:

    # Sem select_related (potencial problema de N+1)
    produtos = Produto.objects.all()
    for produto in produtos:
        print(produto.categoria.nome)  # Gera uma consulta SQL para cada produto
    
    # Com select_related (join SQL em uma única consulta)
    produtos = Produto.objects.select_related('categoria').all()
    for produto in produtos:
        print(produto.categoria.nome)
    Python

    Exemplo com prefetch_related para ManyToMany:

    alunos = Aluno.objects.prefetch_related('cursos').all()
    for aluno in alunos:
        print(aluno.cursos.all())  # Carrega cursos relacionados em uma única consulta
    Python

    Isso reduz drasticamente a quantidade de consultas ao banco de dados, melhorando a performance.

    Manter consultas simples e específicas

    Consultas SQL complexas podem se tornar um gargalo significativo em aplicações maiores. Sempre que possível, mantenha suas consultas simples e diretas. Use agregações e filtros somente quando necessário e prefira trazer apenas os campos que você realmente precisa, ao invés de usar o all().

    Se você precisar de apenas alguns campos, use o método only() ou defer() para carregar apenas os dados essenciais:

    # Carrega apenas os campos 'nome' e 'preco'
    produtos = Produto.objects.only('nome', 'preco')
    Python

    Isso ajuda a reduzir o tempo de resposta e a memória utilizada, especialmente em tabelas com muitos campos.

    Cuidado com migrações destrutivas

    Quando o banco de dados já está em produção, alterações destrutivas — como remover colunas ou renomear tabelas — podem causar problemas graves, especialmente se houver muitos dados em jogo. Por isso, é recomendável que qualquer alteração potencialmente destrutiva seja cuidadosamente planejada.

    Dicas para lidar com migrações destrutivas:

    • Use migrações em fases: Quando possível, execute migrações que envolvem a adição de novos campos e somente depois remova ou altere os campos antigos.
    • Evite downtime: Em sistemas críticos, aplicar uma migração sem parar a aplicação pode ser crucial. Use estratégias de migrações zero-downtime, como adicionar novos campos em uma migração, copiar os dados de forma assíncrona, e só depois remover os campos antigos.

    Otimização de escritas com bulk_create e bulk_update

    Ao inserir ou atualizar muitos registros de uma vez, o uso de bulk_create e bulk_update pode otimizar muito o tempo de execução, evitando que o banco de dados seja acessado repetidamente.

    Exemplo com bulk_create:

    pythonCopiar códigoprodutos = [
        Produto(nome='Produto A', preco=50),
        Produto(nome='Produto B', preco=100),
        Produto(nome='Produto C', preco=150),
    ]
    Produto.objects.bulk_create(produtos)
    Python

    Aqui, o Django fará apenas uma única consulta SQL para inserir todos os produtos, em vez de fazer uma consulta separada para cada produto. O mesmo princípio se aplica ao bulk_update para atualizações em massa.

    Backup e versionamento do banco de dados

    Independentemente do tamanho da sua aplicação, é importante garantir que você tenha backups regulares e versionamento adequado do banco de dados, especialmente antes de aplicar grandes mudanças ou migrações.

    Algumas dicas:

    • Backups automáticos: Configure backups automáticos para que, em caso de falha, você possa restaurar o banco de dados rapidamente.
    • Versionamento de esquema: Use ferramentas como o django-dbbackup ou scripts manuais para manter o controle das versões do banco de dados, principalmente quando ele passa por grandes mudanças.

    Conclusão

    Neste artigo, exploramos os conceitos essenciais para um gerenciamento eficiente de banco de dados em Django, desde os relacionamentos entre modelos até gerenciadores personalizados e migrações. Compreender como esses elementos impactam o desempenho e a organização da aplicação é fundamental para criar projetos escaláveis e bem estruturados.

    Vimos que escolher o tipo certo de relacionamento, como OneToOne, ForeignKey ou ManyToMany, pode melhorar a eficiência das consultas e manter a integridade dos dados. Além disso, gerenciadores personalizados ajudam a centralizar consultas complexas, otimizando o código e o desempenho. Quanto às migrações, o planejamento cuidadoso é essencial para garantir que mudanças no banco de dados sejam seguras e sem problemas em produção.

    Por fim, a adoção de boas práticas — como o uso adequado de índices, a prevenção de consultas N+1, e a simplificação de consultas — garante que sua aplicação Django seja rápida e sustentável à medida que cresce. Gerenciar bem um banco de dados não é só sobre armazenar informações, mas também sobre garantir que o acesso a esses dados seja eficiente e seguro.

  • CBV vs FBV no Django: Quando Usar Cada Uma

    CBV vs FBV no Django: Quando Usar Cada Uma

    Quando começamos a desenvolver uma aplicação em Django, é comum nos depararmos com uma escolha aparentemente simples: usar visualizações baseadas em função (Function-Based Views, FBVs) ou visualizações baseadas em classe (Class-Based Views, CBVs). Para muitos desenvolvedores iniciantes, essa decisão pode parecer trivial, mas ela pode ter implicações significativas no longo prazo, à medida que a complexidade do projeto aumenta.

    As FBVs são, em essência, uma função Python que processa uma requisição e retorna uma resposta. É um modelo mais direto e sem muitos segredos, algo que agrada muito quem está começando. No entanto, à medida que as aplicações se tornam mais complexas e a lógica de visualizações começa a se repetir em várias partes do código, manter tudo dentro de funções simples pode se tornar uma tarefa árdua. Aqui é onde as CBVs entram em cena, oferecendo uma abordagem mais estruturada, baseada em classes e orientada a objetos.

    Mas por que isso é importante? Simples: a escolha entre FBV e CBV pode impactar diretamente a manutenibilidade, reutilização de código, e até a performance da sua aplicação. Imagine que você esteja desenvolvendo um projeto com dezenas ou centenas de endpoints diferentes, muitos dos quais têm lógica semelhante, mas com pequenas variações. Organizar todo esse código sem repetir lógica pode ser um desafio considerável com FBVs.

    Agora, é importante lembrar que o Django não nasceu ontem. O uso de CBVs é um recurso relativamente “recente”, enquanto as FBVs são o método original desde as primeiras versões. Portanto, se você tem um colega mais experiente que insiste em usar FBVs, é provável que ele tenha suas razões — seja uma questão de estilo pessoal ou preferência pela simplicidade de “controlar tudo à mão”.

    Mas antes de fazer essa escolha, é essencial entender as diferenças práticas entre esses dois modelos, seus prós e contras e, o mais importante, quando cada um faz mais sentido no contexto da sua aplicação.

    O que são visualizações baseadas em função (FBVs)?

    Vamos começar pelo básico. Uma visualização baseada em função é literalmente uma função Python que recebe um objeto request e retorna uma resposta HTTP. Isso é tão direto quanto parece. A simplicidade das FBVs é uma de suas maiores vantagens, especialmente para desenvolvedores que ainda não estão familiarizados com conceitos de programação orientada a objetos (POO).

    Aqui está um exemplo bem básico de uma FBV:

    from django.http import HttpResponse
    
    def minha_view(request):
        return HttpResponse("Olá, mundo!")
    Bash

    Esta view faz apenas uma coisa: retorna “Olá, mundo!” como resposta ao receber uma requisição HTTP. Parece um “Hello World” de outro framework? Sim, e essa é a beleza da simplicidade das FBVs. No entanto, como qualquer desenvolvedor mais experiente sabe, aplicações reais são bem mais complicadas do que isso.

    Cenários práticos para FBVs

    As FBVs brilham em cenários onde a lógica de negócio é simples e não há necessidade de reutilização massiva de código entre múltiplas visualizações. Vamos considerar uma aplicação que calcula a média de notas de alunos:

    from django.http import JsonResponse
    
    def calcular_media(request):
        notas = request.GET.getlist('notas')
        try:
            notas = [float(nota) for nota in notas]
            media = sum(notas) / len(notas)
        except ValueError:
            return JsonResponse({'error': 'Por favor, forneça apenas números válidos.'}, status=400)
        
        return JsonResponse({'media': media})
    Bash

    Aqui, a view faz uma operação relativamente simples: recebe uma lista de notas via parâmetros GET, calcula a média e retorna o resultado em formato JSON. Se algo der errado (como notas inválidas), uma mensagem de erro é retornada.

    Uso de decorators em FBVs

    A simplicidade das FBVs permite uma fácil customização utilizando decorators, que podem adicionar funcionalidades à função sem alterar sua lógica principal. Um uso comum de decorators em Django é para controle de acesso. Por exemplo, você pode garantir que uma view só seja acessada por usuários autenticados:

    from django.contrib.auth.decorators import login_required
    
    @login_required
    def dashboard(request):
        return HttpResponse("Bem-vindo ao seu dashboard!")
    Bash

    Aqui, o decorator @login_required verifica se o usuário está autenticado antes de permitir o acesso ao dashboard. Esse é um exemplo clássico de como as FBVs permitem o controle total da lógica de execução com um código simples.

    Vantagens das FBVs

    • Simplicidade e clareza: Como são funções simples, as FBVs são fáceis de entender e rápidas de escrever, sendo uma ótima escolha para protótipos ou aplicações pequenas.
    • Flexibilidade: Você tem controle total sobre o que acontece em cada requisição, o que pode ser uma vantagem em cenários onde você precisa de algo muito específico.
    • Menor overhead: Para views muito simples, as FBVs podem ser ligeiramente mais rápidas, já que não há overhead de herança ou resolução de métodos como nas CBVs.

    Desvantagens das FBVs

    • Escalabilidade: À medida que sua aplicação cresce e as visualizações ficam mais complexas, a organização do código em FBVs pode se tornar um pesadelo. Imagine ter que repetir a lógica de validação de autenticação em várias views sem uma maneira clara de reutilizar esse código.
    • Dificuldade em reutilizar lógica: Embora decorators possam ajudar, não há uma maneira natural de reutilizar pedaços de lógica em múltiplas views, o que pode levar a duplicação de código.

    O que são visualizações baseadas em classe (CBVs)?

    Agora que entendemos o que são FBVs, vamos mergulhar nas visualizações baseadas em classe. Diferentemente das FBVs, as CBVs utilizam conceitos da Programação Orientada a Objetos (POO). Em vez de funções, trabalhamos com classes que herdam de uma classe base, fornecida pelo Django, e implementam métodos para lidar com os diferentes tipos de requisição (GET, POST, PUT, etc.).

    Aqui está um exemplo básico de uma CBV:

    from django.http import HttpResponse
    from django.views import View
    
    class MinhaView(View):
        def get(self, request):
            return HttpResponse("Olá, mundo!")
    Bash

    Neste exemplo, MinhaView é uma classe que herda da classe View do Django. O método get é chamado quando a view recebe uma requisição HTTP GET. Assim como nas FBVs, o objetivo aqui é retornar “Olá, mundo!”, mas agora dentro da estrutura de uma classe.

    CBVs Genéricas

    O verdadeiro poder das CBVs vem das views genéricas, que o Django fornece para acelerar o desenvolvimento de aplicações comuns. Por exemplo, imagine que você queira criar uma página que exiba uma lista de objetos de um determinado modelo. Com FBVs, você teria que escrever a lógica para buscar os objetos do banco de dados, passá-los para o contexto e renderizar o template manualmente. Com CBVs genéricas, isso é muito mais fácil:

    from django.views.generic import ListView
    from .models import Aluno
    
    class ListaAlunosView(ListView):
        model = Aluno
        template_name = 'alunos/lista.html'
    Bash

    Neste exemplo, ListaAlunosView é uma CBV genérica que automaticamente busca todos os objetos do modelo Aluno, os insere no contexto e renderiza o template lista.html. Tudo isso sem que você precise escrever a lógica repetitiva de consulta ao banco de dados e renderização do template.

    Mixins em CBVs

    Uma das maiores vantagens das CBVs é a capacidade de reutilizar lógica por meio de mixins. Mixins são classes que você pode “misturar” em suas CBVs para adicionar funcionalidades específicas sem repetir código.

    Por exemplo, digamos que você queira garantir que apenas usuários autenticados possam acessar uma determinada view. Em vez de usar um decorator como fizemos nas FBVs, podemos usar o LoginRequiredMixin:

    from django.contrib.auth.mixins import LoginRequiredMixin
    from django.views.generic import TemplateView
    
    class DashboardView(LoginRequiredMixin, TemplateView):
        template_name = 'dashboard.html'
    Bash

    Aqui, LoginRequiredMixin faz com que a view só seja acessível para usuários autenticados. O uso de mixins permite que você adicione funcionalidades comuns a várias views sem precisar repetir código, tornando suas CBVs extremamente flexíveis e reutilizáveis.

    Vantagens das CBVs

    • Reuso de código: A herança de classes e o uso de mixins tornam as CBVs muito mais eficientes em termos de reutilização de lógica. Se você tem várias views que compartilham funcionalidades, as CBVs são a escolha ideal.
    • Organização: Ao dividir a lógica de cada tipo de requisição em métodos (get(), post(), etc.), o código se torna mais organizado e fácil de entender.
    • Extensibilidade: CBVs permitem que você crie hierarquias de classes, estenda funcionalidades e adicione comportamentos personalizados sem duplicar código.

    Desvantagens das CBVs

    • Curva de aprendizado: Para desenvolvedores iniciantes, a programação orientada a objetos pode ser mais difícil de entender, especialmente quando comparada à simplicidade das FBVs.
    • Abstração excessiva: Em alguns casos, as CBVs fazem muitas coisas “automáticas”, o que pode tornar o comportamento da aplicação mais difícil de entender e depurar.

    Diferenças práticas entre CBVs e FBVs

    Agora que já abordamos as definições e exemplos de visualizações baseadas em função (FBVs) e visualizações baseadas em classe (CBVs), chegou a hora de fazer uma comparação mais profunda entre as duas abordagens. Vamos explorar aspectos como performance, facilidade de uso, reutilização de código, e manutenibilidade para ajudar a determinar qual delas é mais adequada em diferentes situações.

    Clareza e Simplicidade

    Como vimos nos exemplos anteriores, as FBVs são incrivelmente diretas. O fluxo de código em uma FBV é linear: a função recebe uma requisição, executa uma lógica e retorna uma resposta. Se o seu projeto é pequeno e você está lidando com operações simples, essa abordagem pode ser a mais produtiva, pois não há muitas abstrações a serem compreendidas.

    Por exemplo, em uma view onde você só precisa retornar um JSON simples baseado em parâmetros de URL, as FBVs são uma solução rápida e fácil:

    from django.http import JsonResponse
    
    def calcular_soma(request):
        numeros = request.GET.getlist('numeros')
        soma = sum([int(num) for num in numeros])
        return JsonResponse({'soma': soma})
    Bash

    Não há necessidade de classes, herança, ou qualquer outro conceito adicional. O código está todo ali, e é fácil de seguir.

    Já as CBVs oferecem uma abordagem mais robusta e escalável, mas podem parecer complexas no início. O simples ato de criar uma classe, implementar métodos e seguir o padrão da POO já adiciona um nível extra de “peso cognitivo” ao código, o que pode ser um empecilho para novos desenvolvedores ou para quem está acostumado com uma abordagem mais funcional.

    from django.http import JsonResponse
    from django.views import View
    
    class CalcularSomaView(View):
        def get(self, request):
            numeros = request.GET.getlist('numeros')
            soma = sum([int(num) for num in numeros])
            return JsonResponse({'soma': soma})
    Bash

    Embora este exemplo com CBV pareça similar à FBV equivalente, o benefício da CBV fica evidente em cenários mais complexos, como quando você precisa de suporte para múltiplos tipos de requisição (GET, POST, etc.) ou quando a lógica da view precisa ser reutilizada em várias partes do código.

    Reutilização de Código

    Um dos pontos mais fortes das CBVs é a possibilidade de reutilizar código de maneira muito mais eficiente do que nas FBVs. Com a capacidade de herdar de classes e utilizar mixins, as CBVs permitem criar estruturas modulares e reutilizáveis, o que é particularmente útil em projetos de médio a grande porte.

    Por exemplo, se você tem várias views que exigem autenticação e realizam operações semelhantes, pode criar uma classe base que contenha toda a lógica comum e herdar essa classe nas suas views específicas. Isso economiza tempo e evita duplicação de código:

    from django.contrib.auth.mixins import LoginRequiredMixin
    from django.views.generic import View
    
    class BaseProtectedView(LoginRequiredMixin, View):
        def dispatch(self, request, *args, **kwargs):
            # Lógica comum de autenticação ou validação
            return super().dispatch(request, *args, **kwargs)
    
    class MinhaView(BaseProtectedView):
        def get(self, request):
            return HttpResponse("Acesso autorizado!")
    Bash

    Aqui, BaseProtectedView é uma classe base que impõe a verificação de autenticação e pode conter qualquer lógica comum às views protegidas. A view MinhaView herda toda essa funcionalidade sem precisar reescrevê-la. Com FBVs, essa abordagem seria mais complexa e provavelmente envolveria duplicação de código ou a necessidade de usar múltiplos decorators, o que nem sempre é ideal.

    As FBVs, por outro lado, não oferecem suporte nativo para esse tipo de reutilização. Se você precisar reutilizar lógica entre várias FBVs, terá que recorrer a soluções alternativas, como decorators ou funções auxiliares, o que pode acabar tornando o código menos legível.

    Organização e Extensibilidade

    A organização do código em CBVs é naturalmente mais estruturada, graças à separação clara entre diferentes tipos de requisição e à organização da lógica em métodos distintos. Quando você usa uma CBV, sabe exatamente onde encontrar a lógica de uma requisição GET, POST, etc., pois cada uma dessas ações será implementada em um método específico da classe.

    Exemplo de uma CBV que lida com múltiplos tipos de requisição:

    from django.http import JsonResponse
    from django.views import View
    
    class OperacoesView(View):
        def get(self, request):
            return JsonResponse({"message": "Requisição GET recebida"})
    
        def post(self, request):
            return JsonResponse({"message": "Requisição POST recebida"})
    
        def put(self, request):
            return JsonResponse({"message": "Requisição PUT recebida"})
    Bash

    Essa separação ajuda a manter o código limpo e mais fácil de manter, especialmente em projetos grandes, onde uma mesma view pode ter que lidar com diferentes tipos de requisição.

    Nas FBVs, por outro lado, toda a lógica para diferentes tipos de requisição precisaria ser tratada dentro da mesma função, o que pode rapidamente se tornar confuso. Um exemplo seria:

    def operacoes_view(request):
        if request.method == "GET":
            return JsonResponse({"message": "Requisição GET recebida"})
        elif request.method == "POST":
            return JsonResponse({"message": "Requisição POST recebida"})
        elif request.method == "PUT":
            return JsonResponse({"message": "Requisição PUT recebida"})
        else:
            return JsonResponse({"message": "Método não suportado"}, status=405)
    Bash

    Neste caso, o código pode ser mais difícil de seguir à medida que a complexidade aumenta. O fato de termos que lidar com todos os métodos HTTP em uma única função faz com que a organização do código se deteriore rapidamente conforme mais lógica é adicionada.

    Performance e Overhead

    Quando se fala em performance, as FBVs têm uma leve vantagem em termos de overhead. Como as FBVs são apenas funções, não há sobrecarga adicional de herança de classes, resolução de métodos e outras funcionalidades que o Django precisa gerenciar quando você usa CBVs.

    No entanto, para a maioria das aplicações modernas, essa diferença de performance é insignificante. A menos que você esteja lidando com um projeto de altíssima performance, como um sistema de tempo real com centenas de milhares de requisições simultâneas, a diferença entre CBVs e FBVs no quesito performance é praticamente imperceptível.

    Além disso, o ganho em reusabilidade, organização e manutenção que as CBVs proporcionam muitas vezes compensa o pequeno overhead adicional. O uso de CBVs em aplicações de médio e grande porte pode até melhorar a performance geral do projeto a longo prazo, ao evitar duplicação de código e promover uma melhor organização da lógica.

    Testabilidade e Debugging

    Quando se trata de testabilidade, as FBVs tendem a ser mais fáceis de testar, especialmente em casos simples. Como são apenas funções, você pode isolar a lógica com facilidade e escrever testes unitários sem ter que se preocupar com o estado da classe ou com herança.

    Exemplo de um teste simples de uma FBV:

    from django.test import TestCase
    from django.http import HttpRequest
    from .views import calcular_soma
    
    class TesteFBV(TestCase):
        def test_calcular_soma(self):
            request = HttpRequest()
            request.GET['numeros'] = ['1', '2', '3']
            response = calcular_soma(request)
            self.assertEqual(response.json()['soma'], 6)
    Bash

    Por outro lado, as CBVs exigem mais cuidado ao testar, pois você pode ter que lidar com a herança e o estado da classe em si. No entanto, a separação entre métodos (GET, POST, etc.) nas CBVs pode tornar o teste de cada parte da lógica individualmente mais fácil, já que cada método geralmente contém uma funcionalidade específica.

    Aqui está um exemplo de um teste para uma CBV:

    from django.test import TestCase
    from django.http import HttpRequest
    from .views import OperacoesView
    
    class TesteCBV(TestCase):
        def test_get(self):
            request = HttpRequest()
            request.method = 'GET'
            response = OperacoesView.as_view()(request)
            self.assertEqual(response.status_code, 200)
            self.assertEqual(response.json()['message'], "Requisição GET recebida")
    Bash

    Em relação ao debugging, as FBVs também podem ser mais fáceis de depurar devido à sua simplicidade. No entanto, as CBVs podem se tornar complexas quando múltiplas classes e mixins estão envolvidos, e entender o fluxo de execução pode exigir um conhecimento mais profundo de como o Django resolve as heranças e chamadas de métodos. Para resolver isso, o uso de ferramentas de depuração como o pdb ou django debug toolbar pode ser essencial.

    Quando usar CBVs ou FBVs?

    Chegamos à pergunta central deste artigo: Quando usar CBVs e quando usar FBVs? A resposta, como em muitas questões de desenvolvimento de software, é “depende”. Ambas as abordagens têm seus méritos e são adequadas para diferentes cenários.

    Quando optar pelas FBVs?

    • Simplicidade é essencial: Se você está desenvolvendo uma aplicação simples, onde as visualizações não precisam de muita lógica de reutilização e herança, as FBVs podem ser a escolha mais prática.
    • Prototipagem rápida: Para projetos pequenos ou protótipos, onde você precisa de resultados rápidos, as FBVs podem ser mais fáceis de implementar e depurar.
    • Pequenas operações: Se suas visualizações estão limitadas a executar operações muito específicas e não envolvem uma lógica complexa que precise ser reutilizada, as FBVs podem ser mais adequadas.

    Quando optar pelas CBVs?

    • Aplicações complexas: Se você está trabalhando em uma aplicação de médio a grande porte, onde a modularidade, reutilização de código e organização são essenciais, as CBVs são uma escolha melhor.
    • Reutilização de lógica: Se várias visualizações compartilham partes significativas de lógica (como validação, autenticação, etc.), as CBVs são ideais, pois permitem a criação de classes base e o uso de mixins.
    • Manutenção a longo prazo: Se o seu projeto vai crescer e ser mantido por um longo período, o uso de CBVs pode facilitar a manutenibilidade, especialmente em equipes grandes, onde a clareza e a organização são críticas.

    Conclusão

    As visualizações baseadas em função (FBVs) e as visualizações baseadas em classe (CBVs) têm suas vantagens e desvantagens. A escolha entre uma ou outra depende do contexto do projeto, das necessidades de reutilização de código, da complexidade da lógica da view e da sua familiaridade com POO.

    Para projetos pequenos e simples, onde a lógica de visualização é clara e não precisa ser reutilizada, as FBVs são uma solução direta e eficaz. No entanto, em projetos maiores ou em crescimento, as CBVs oferecem uma estrutura mais organizada e reutilizável, permitindo uma manutenção mais eficiente e a criação de visualizações modulares.

    Em última análise, o importante é lembrar que não há uma escolha “correta” entre FBVs e CBVs — há apenas a escolha certa para o seu projeto e para o seu contexto.

    Então, ao se deparar com a famosa pergunta “FBV ou CBV?”, agora você tem a base necessária para responder de forma mais confiante: “Depende do contexto!”


    Bônus: Explorando Class-Based Views com Classy CBV

    Se você está trabalhando com visualizações baseadas em classe (CBVs) no Django, o site Classy Class-Based Views (CCBV) é uma ferramenta indispensável. Ele organiza de forma clara todas as CBVs do Django, mostrando suas heranças, métodos, e como cada uma delas pode ser usada e personalizada.

    Com o CCBV, você pode:

    • Entender rapidamente a hierarquia de herança: Veja como as CBVs genéricas são estruturadas e quais métodos você pode sobrescrever.
    • Explorar métodos e funcionalidades: O site detalha quais métodos cada CBV oferece, ajudando você a encontrar a melhor forma de personalizar suas views sem perder tempo.
    • Facilitar a customização: Se você quer ajustar o comportamento de uma ListView, por exemplo, o CCBV mostra exatamente onde e como você pode fazer isso, como sobrescrever o método get_queryset() para filtrar dados.

    Ao invés de vasculhar a documentação oficial ou o código-fonte do Django, o CCBV te dá uma visão prática e rápida para melhorar sua produtividade e confiança no desenvolvimento com CBVs. Se você ainda não conhece, vale a pena visitar: Classy CBV.

  • Formulários no Django: Manipulação, Validação e Renderização

    Formulários no Django: Manipulação, Validação e Renderização

    Formulários no Django são uma parte essencial de qualquer aplicação web que precise interagir com o usuário. No Django, o sistema de formulários é um dos recursos mais poderosos e flexíveis, facilitando o trabalho com dados de entrada, sua validação e posterior manipulação.

    Se você já precisou lidar com formulários HTML manualmente, sabe o quão trabalhoso pode ser garantir que todos os dados sejam capturados e validados corretamente.

    É aí que entra o Django, automatizando grande parte desse processo e permitindo que você foque no desenvolvimento da lógica da sua aplicação.

    Esta postagem explora a manipulação de formulários no Django, incluindo a criação, validação e renderização, com exemplos práticos para ilustrar cada etapa.

    Criando um Formulário no Django

    Django fornece uma classe chamada forms.Form, que facilita a criação e manipulação de formulários. Além disso, o Django oferece o forms.ModelForm, que automaticamente gera um formulário baseado em um modelo (Model) definido no banco de dados. Comecemos pela criação de um formulário simples usando forms.Form.

    Exemplo básico de criação de formulário

    from django import forms
    
    class ContatoForm(forms.Form):
        nome = forms.CharField(label='Nome', max_length=100)
        email = forms.EmailField(label='E-mail')
        mensagem = forms.CharField(widget=forms.Textarea, label='Mensagem')
    
    Python

    No exemplo acima, definimos um formulário de contato com três campos:

    • nome: Um campo de texto simples (usando CharField) com um limite de 100 caracteres.
    • email: Um campo de e-mail que automaticamente valida se o dado inserido é um endereço de e-mail válido.
    • mensagem: Um campo de texto maior, onde usamos forms.Textarea para renderizar um
      class ContatoForm(forms.Form):
          nome = forms.CharField(label='Nome', max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'}))
          email = forms.EmailField(label='E-mail', widget=forms.EmailInput(attrs={'class': 'form-control'}))
          mensagem = forms.CharField(widget=forms.Textarea(attrs={'rows': 5, 'class': 'form-control'}), label='Mensagem')
      
      Python

    Aqui usamos o argumento attrs para adicionar classes CSS personalizadas a cada campo, que seriam úteis ao integrar com frameworks como Bootstrap, por exemplo.

    Em outras palavras, os widgets te da a oportunidade o que você poderá adicionar dentro dos inputs que serão renderizados no HTML.

    Tipos de campos no Formulários no Django

    Django oferece uma grande variedade de campos para formulários, cada um com uma função específica. Alguns dos mais usados incluem:

    • CharField: Usado para entradas de texto simples.
    • EmailField: Valida se o valor inserido é um e-mail válido.
    • IntegerField: Valida se o valor inserido é um número inteiro.
    • DateField: Para campos de data.
    • BooleanField: Um checkbox booleano.

    Exemplo de um formulário com diferentes tipos de campo:

    class RegistroForm(forms.Form):
        nome = forms.CharField(label='Nome', max_length=100)
        idade = forms.IntegerField(label='Idade')
        data_nascimento = forms.DateField(label='Data de Nascimento')
        aceita_termos = forms.BooleanField(label='Aceito os termos e condições')
    Python

    Caso você viu a ultima postagem que fiz sobre models você perceberá que os campos de formulários do Django são parecidos em termos de “tipagem”. Exatamente, como nem sempre usaremos formulários para fazer inserção de dados para o banco de dados e melhor ter forms e models separados, embora sejam parecidos. 😉

    Validação de Formulários

    Django oferece validação automática de dados para os tipos de campo. No entanto, existem cenários onde você precisará implementar validações personalizadas para os seus formulários. A validação ocorre em dois níveis:

    1. Validação embutida: Django já inclui regras de validação padrão para muitos tipos de campos (por exemplo, EmailField verifica se o valor inserido é um e-mail válido).
    2. Validação personalizada: Você pode criar suas próprias regras de validação para campos específicos.

    Validação embutida nos formulários no Django

    Sempre que você utiliza um tipo de campo como EmailField, IntegerField, etc., o Django automaticamente aplica validações apropriadas ao valor recebido.

    Por exemplo, se tentarmos enviar um valor inválido para o campo de e-mail no formulário de contato que criamos acima, Django nos retornará um erro de validação:

    form = ContatoForm({'nome': 'João', 'email': 'emailinvalido', 'mensagem': 'Olá!'})
    if form.is_valid():
        # Este bloco não será executado
        print("Formulário válido")
    else:
        print(form.errors)
    
    Python

    Aqui, como o e-mail é inválido, o Django vai gerar um erro como parte do dicionário form.errors que poderá ser exibido no template.

    Validação personalizada nos formulários no Django

    Além da validação embutida, você pode definir suas próprias regras de validação para campos individuais. Isso é feito através do método clean_(), onde você pode especificar a lógica de validação adicional para um determinado campo.

    Vamos criar um exemplo onde validamos o nome de usuário para garantir que ele tenha pelo menos 5 caracteres:

    class RegistroForm(forms.Form):
        nome = forms.CharField(label='Nome', max_length=100)
        email = forms.EmailField(label='E-mail')
    
        def clean_nome(self): # <-- Aqui você esta adicionando validação ao campo "nome" 😉 
            nome = self.cleaned_data.get('nome')
            if len(nome) < 5:
                raise forms.ValidationError("O nome deve ter pelo menos 5 caracteres.")
            return nome
    
    Python

    Aqui, o método clean_nome será executado automaticamente durante o processo de validação do formulário. Se o nome for menor que 5 caracteres, Django gerará um erro de validação.

    https://wordpress.vps9144.panel.icontainer.cloud/introducao-ao-framework-django-compreendendo-o-framework-e-sua-arquitetura/#comments

    Validação geral de formulário

    Além da validação de campos individuais, você também pode realizar validações no nível do formulário. Isso é feito sobrescrevendo o método clean() da classe do formulário “Form”. Isso é útil quando você precisa validar dois ou mais campos em conjunto.

    Exemplo de validação no nível do formulário:

    class RegistroForm(forms.Form):
        senha = forms.CharField(widget=forms.PasswordInput)
        confirma_senha = forms.CharField(widget=forms.PasswordInput)
    
        def clean(self):
            cleaned_data = super().clean()
            senha = cleaned_data.get("senha")
            confirma_senha = cleaned_data.get("confirma_senha")
    
            if senha and confirma_senha and senha != confirma_senha:
                raise forms.ValidationError("As senhas não coincidem.")
    
    Python

    Neste exemplo, garantimos que os campos senha e confirma_senha correspondam. Se não forem iguais, um erro de validação será gerado.

    Renderizando Formulários no Django

    Uma vez que criamos e validamos um formulário, o próximo passo é renderizá-lo em um template HTML. No Django, isso pode ser feito de forma bastante simples usando o objeto form que é passado para o template a partir da view.

    Exemplo básico de renderização de formulário

    No arquivo views.py, passamos uma instância do formulário para o contexto:

    from django.shortcuts import render
    from .forms import ContatoForm
    
    def contato_view(request):
        form = ContatoForm()
        return render(request, 'contato.html', {'form': form})
    Python

    No template contato.html, podemos renderizar o formulário de diferentes maneiras. A forma mais simples é usando {{ form.as_p }}, que renderiza cada campo como um parágrafo (

    ):

    <form method="post">
        {% csrf_token %}
        {{ form.as_p }} <-- Aqui todas os campos serão adicionados. 
        <button type="submit">Enviarbutton>
    form>
    HTML

    Você também pode renderizar manualmente cada campo do formulário, o que oferece mais controle sobre o HTML gerado:

    <form method="post">
        {% csrf_token %}
        <div>
            {{ form.nome.label_tag }}
            {{ form.nome }}
            {{ form.nome.errors }}
        div>
        <div>
            {{ form.email.label_tag }}
            {{ form.email }}
            {{ form.email.errors }}
        div>
        <div>
            {{ form.mensagem.label_tag }}
            {{ form.mensagem }}
            {{ form.mensagem.errors }}
        div>
        <button type="submit">Enviarbutton>
    form>
    HTML

    Ao renderizar cada campo manualmente, você também pode exibir os erros de validação associados diretamente ao lado de cada campo, como mostrado acima com {{ form.campo.errors }}.

    Fluxograma Explicando Os Passos Do Formulário No Django

    Conclusão

    Formulários no Django são uma ferramenta poderosa para lidar com dados de entrada de usuários, validação e renderização em HTML.

    Neste post, você aprendeu como criar formulários, validar dados tanto de forma embutida quanto personalizada, e finalmente renderizar o formulário no template HTML. Dominar esse recurso do Django simplifica a coleta de dados em sua aplicação, além de garantir que os dados sejam válidos e seguros.

  • Como Criar Um Pacote Django Privado

    Como Criar Um Pacote Django Privado

    Neste material, detalharei como você pode criar seu próprio pacote Django privado para ser reutilizado entre diferentes projetos.

    Portanto, para acompanhar, usaremos um aplicativo chamado cart que tem a função de manipular pedidos personalizados enviando-os para checkout. Este aplicativo será transformado em um pacote para ser usado por outros projetos Django, onde todos os passos serão explicados para você.

    Diretorio Inicial

    seu_pacote_django/
        └───cart/
            │   admin.py
            │   apps.py
            │   contexts.py
            │   models.py
            │   urls.py
            │   views.py
            │   __init__.py
            │   
            ├───migrations/
            │       __init__.py
            │       
            ├───templates/
            │       cart.html
            │       header2.html
            │
            └───tests/
                    test_urls.py
                    test_views.py
                    __init__.py
    Bash

    Como você pode ver, o diretório inicial tem apenas o aplicativo do carrinho dentro dele. Portanto, para iniciar seu pacote, você precisará criar mais arquivos que lidarão com o processo de construção do pacote, bem como alguns requisitos que você precisará caso, algum dia, queira torná-lo público, talvez adicionando-o ao PYPI.org.

    Arquivos Importantes Para Construir o Pacote Django:

    Nesta seção serão detalhados os arquivos necessários para construir o pacote do carrinho. Portanto, para facilitar o acompanhamento, a instrução será seguida como nome do arquivo + instrução + código.

    1. Arquivo Setup.py

    Este é o arquivo mais importante. setup.py representa main quando você chama as funções de instalação. Basicamente, dentro do setup.py você deve adicionar todas as informações necessárias para criar seu pacote. Como o nome do pacote, versão, licença e etc:

    # setup.py
    
    import os
    from setuptools import find_packages, setup
    
    with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme:
        README = readme.read()
    
    # allow setup.py to be run from any path
    os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
    
    setup(
        name='cart',
        version='0.0.1',
        packages=find_packages(),
        include_package_data=True,
        license='Your license', 
        description='A cart package that handles payments with stripe.',
        long_description=README,
        url='yoursite.com',
        author='you',
        author_email='your@email.com',
        classifiers=[
            'Environment :: Web Environment',
            'Framework :: Django',
            'Framework :: Django :: 3.2',  
            'Intended Audience :: For my own or company usage',
            'License :: OSI Approved :: Your License',  
            'Operating System :: OS Independent',
            'Programming Language :: Python',
            'Programming Language :: Python :: 3',
            'Programming Language :: Python :: 3.6',
            'Programming Language :: Python :: 3.8',
            'Programming Language :: Python :: 3.9',
            'Topic :: Internet :: WWW/HTTP',
            'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
        ],
    )
    Python

    Existem outras maneiras de configurar o arquivo setup.py com abordagens diferentes. No entanto, eu sugeriria que esta é a melhor e mais fácil maneira de criar seu arquivo setup.py.

    2. README.rst file

    De acordo com fileinfo.com, a extensão .rst ou linguagem de marcação reStructuredText é um arquivo de texto que contém e suporta código escrito e estilizado. No site Sphinx, é detalhado que a extensão .rst é a principal linguagem de marcação para a criação e design de documentação de código. No entanto, este não é um arquivo realmente necessário para criar seu pacote privado, mas é essencial se um dia você quiser torná-lo público no pypi.org.

    clique em view raw para ver o formato de marcação.

    Your Django Package
    To install:
    
    pip install git+https://github.com/your_name/your_cart_package.git
    In your_project.settings.py add:
    
    INSTALLED_APPS = [
        ...
        'cart',
        ...
    ]
    Migrate the changes from the underwriting_manual app:
    
    $ python manage.py makemigrations cart
    
    $ python manage.py migrate cart
    In your_project.urls.py add:
    
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/', include('core.urls')),
        path('cart/', include('cart.urls')), # <=====
    ]
    RST

    3. Arquivo LICENSE.

    De acordo com a esri, um arquivo de licença é um arquivo de texto simples que fornece automaticamente as informações necessárias — como nome do produto, número de autorização e informações de contato do usuário — Software Authorization Wizard.

    Dependendo do nível de informação que você tem em seu aplicativo, eu aconselho você a ter um arquivo LICENSE bem adaptado para evitar qualquer problema no futuro. No entanto, o arquivo LICENSE também é essencial ao carregar seu pacote para pypi.org.

    /* Copyright (C) Your Company Systems, Inc - All Rights Reserved
     * Unauthorized copying of this file, via any medium is strictly prohibited
     * Proprietary and confidential
     * Written by your company attorney <you@yourcompany.com>, December 2021
     */
    Markdown

    Neste exemplo, usarei um modelo personalizado que pode ser editado por você:

    PS: Não sou advogado, portanto, o exemplo de licença não pode ser usado como um conselho ou exemplo do mundo real.

    4. MANIFEST.in file

    MANIFEST.in é um arquivo de texto que contém uma série de “comandos” em um formato definido pelo Distutils. Os comandos manifest permitem que você inclua ou exclua arquivos e diretórios específicos. Principalmente aqueles que não são reconhecidos pelo python como um diretório porque não contêm o init.py, como o diretório templates/ no Django. E arquivos que não têm a extensão .py e os arquivos que são usados ​​para construir o pacote, como LICENSE e README.rst.

    No entanto, o MANIFEST.in é basicamente um conjunto de comandos para incluir no pacote que será reconhecido pelo sdist ao construir seu pacote. Portanto, quando o pacote for criado, se um diretório ou arquivo for especificado, eles estarão lá quando o pacote for construído 😉.

    include LICENSE
    include README.rst
    recursive-include cart/templates *
    recursive-include cart/tests *
    INI

    Buildando o Pacote Django

    No momento em que você está seguindo este tutorial, no qual iniciamos apenas com o aplicativo cart , ele mudou. Portanto, se você fez tudo corretamente, seu diretório de trabalho deve ficar assim:

    seu_pacote_django/
      │   LICENSE
      │   MANIFEST.in
      │   README.rst
      │   setup.py
      │
      └───cart/
          │   admin.py
          │   apps.py
          │   contexts.py
          │   models.py
          │   urls.py
          │   views.py
          │   __init__.py
          │
          ├───migrations/
          │       __init__.py
          │
          ├───templates/
          │       cart.html
          │       header2.html
          │
          └───tests/
                  test_urls.py
                  test_views.py
                  __init__.py
    Bash

    Agora, há alguns passos que precisam ser seguidos para iniciar o processo de construção do seu pacote:

    Instale os pacotes sdist (distribuição de origem) e whell:

    pip install sdist && pip install wheel
    Bash

    nota: Eu consideraria criar um ambiente virtual e instalar os pacotes sdist e whell nele (não se esqueça de adicionar um arquivo .gitignore).

    Build do pacote Em Ação:

    No diretório raiz do pacote, adicione os seguintes comandos para finalmente compilar o pacote:

    python setup.py sdist && python setup.py bdist_wheel
    Bash

    Dessa forma o pacote será finalmente construído deixando você com o seguinte diretório:

    seu_pacote_django/
    │   .gitignore
    │   LICENSE
    │   MANIFEST.in
    │   README.rst
    │   setup.py
    │
    ├───build/
    │   ├───bdist.win-amd64
    │   └───lib
    │       └───cart/
    │           │   admin.py
    │           │   apps.py
    │           │   contexts.py
    │           │   models.py
    │           │   urls.py
    │           │   views.py
    │           │   __init__.py
    │           │
    │           ├───migrations/
    │           │       __init__.py
    │           │
    │           ├───templates/
    │           │       cart.html
    │           │       header2.html
    │           │
    │           └───tests/
    │                   test_urls.py
    │                   test_views.py
    │                   __init__.py
    │
    ├───cart/
    │   │   admin.py
    │   │   apps.py
    │   │   contexts.py
    │   │   models.py
    │   │   urls.py
    │   │   views.py
    │   │   __init__.py
    │   │
    │   ├───migrations/
    │   │       __init__.py
    │   │
    │   ├───templates/
    │   │       cart.html
    │   │       header2.html
    │   │
    │   └───tests/
    │           test_urls.py
    │           test_views.py
    │           __init__.py
    │
    ├───cart.egg-info/
    │       dependency_links.txt
    │       PKG-INFO
    │       SOURCES.txt
    │       top_level.txt
    │
    └───dist/
            cart-0.0.1-py3-none-any.whl
            cart-0.0.1.tar.gz
    Bash

    Instalando Seu Proprio Pacote Django

    O próximo passo para usar seu pacote é enviá-lo para um repositório do github para ser usado por você ou sua empresa.

    Então, você pode instalar o pacote pelo seguinte comando:

    Para repositório privado:

    pip install git+ssh://git@github.com:sua_conta/seu_pacote_django.git
    Bash

    Para repositório público:

    pip install git+https://github.com/sua_conta/seu_pacote_django.git
    Bash

    Faça uma pip list para verificar se o pacote do carrinho está instalado no seu ambiente virtual.

    $ pip list
    Package    Version
    ---------- -------
    pip        21.2.4
    cart        0.0.1   <-- Aqui
    Bash

    Portanto, seguindo este tutorial você aprendeu como construir seu próprio pacote Django que pode ser reutilizado entre diferentes projetos. Por último, mas não menos importante, embora este tutorial tenha exemplificado um aplicativo Django que foi transformado em um pacote, esta abordagem que você aprendeu tem o mesmo processo para criar qualquer pacote Python.

  • Testes Unitários Com Django

    Testes Unitários Com Django

    Testes unitários são basicamente umas das formas de saber se o código da aplicação que está sendo construída está no caminho correto, baseado nos requerimentos.

    Normalmente, e de forma correta, se constrói os testes para depois criar o código da aplicação, usando o teste em si como referência de funcionalidade.

    Como os testes funcionam

    Demorou para entrar na minha cabeça como os testes unitários funcionam. Eu sempre me perguntava: — Como assim, primeiro você cria os testes para depois criar o código? Isso não entra na minha cabeça!

    Isso é bem verdade, porém vamos usar uma analogia usando como exemplo uma fábrica metalúrgica com uma linha de montagem:

    Por exemplo uma empresa recebe o requisito para criar uma peça com todas as dimensões como requisitos, altura, largura e também o diâmetro da parede da mesma.

    Essas requisições são super importantes para que a peça se encaixe corretamente em um motor ou outra engrenagem, sendo assim, ficando perfeitamente adequada para os requisitos do cliente.

    Testes unitários

    Sendo torneiro mecânico em 2008 precisei usar testes para praticamente todas as peças que eu produzia para que as mesmas ficassem dentro do padrão do desenho/gabarito enviado pelo cliente.

    Era usado um molde com as especificações corretas onde se encaixavam as peças que eram feitas. Assim, junto com as medidas especificadas se sabia se o produto feito estava correto.

    Dessa forma ficou mais fácil entender o porquê de criar os testes primeiro para depois o código. Os testes são como o gabarito, o código são as peças criadas que serão testadas baseadas nos testes criados anteriormente (gabarito). Agora, creio que ficou mais fácil entender.

    Testando uma aplicação REST API Django

    Agora, vamos supor que recebemos um requisito para dois endpoints de uma aplicação de turismo:

    • Comments
    • Destinations

    Os requisitos são como a seguir:

    Como podemos ver, o modelo Comment tem um relacionamento de um para muitos com a tabela Destinations assim como os seus respectivos campos. Sendo assim, iniciaremos os testes para os modelos representados acima:

    tourism/tests/test_models.py

    # tourism/tests/test_models.py
    
    from django.test import TestCase
    from .models import Destinations, Comment
    from django.utils import timezone
    
    class TestModels(TestCase):
    
        def test_destinations_model(self):
            #test if Destinations model is passing correctly its values
            destination = Destinations.objects.create(
                tour_title = 'test1',
                booking_start_date='2021-05-30',
                booking_end_date= '2022-05-30',
                price=2000,
                description='test1',
                author='test1',
            )
            destination.save()
            self.assertEquals(destination.tour_title, 'test1')
            self.assertEquals(destination.booking_start_date, '2021-05-30')
            self.assertEquals(destination.booking_end_date, '2022-05-30')
            self.assertEquals(destination.price, 2000)
            self.assertEquals(destination.description, 'test1')
            self.assertEquals(destination.author, 'test1')
    
        def test_comment_model(self):
            #test if Comment model is related to Destinations model
            destination = Destinations.objects.create(
                tour_title = 'test1',
                booking_start_date ='2021-05-30',
                booking_end_date= '2022-05-30',
                price = 2000,
                description = 'test1',
                author = 'test1',
            )
            destination.save()
            comment = Comment.objects.create(
                #the relation between both models
                post = destination,
                name = 'John',
                email = 'any@email.com',
                comment = 'test1',
                created_on = timezone.now(),
                active = False,
            )
            comment.save()
            self.assertEquals(comment.post, destination)
    Python

    Como visto acima, foi herdada a classe TestCase do Django para poder testar os dois modelos Comment e Destinations na classe TestModels.

    Na função test_destinations_model(self) foi alocado o modelo Destinations em seu escopo, com uma query de criação feita no banco de dados de testes, usando o ORM do Django e com seus respectivos campos.

    https://wordpress.vps9144.panel.icontainer.cloud/introducao-ao-framework-django-compreendendo-o-framework-e-sua-arquitetura/

    Logo abaixo, é usado a função asserEquals(param1, param2) para saber se a mesma está sendo criada corretamente. Neste caso a função assertEquals() serve como o gabarito exemplificado na analogia e exemplo mencionado acima, para testar se o mesmo está sendo criado como deveria no banco de dados de testes.

    Vamos testar para ver se o teste retorna como correto, “OK”:

    python manage.py test
    Found 1 test(s).
    System check identified no issues (0 silenced).
    E
    ====================================================================
    ERROR: tourism.tests (unittest.loader._FailedTest)
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    ImportError: Failed to import test module: tourism.tests
    Traceback (most recent call last):
    …
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    Ran 1 test in 0.000s
    Bash

    Como visto, ao tentar rodar os testes o mesmo não funcionou pois não tem oque testar ainda, as classes/tabelas ainda não foram criadas.

    Agora vamos criar as tabelas como no requerimento e seus consecutivos relacionamentos e rodar os testes novamente:

    tourism/models.py:

    # tourism/models.py
    
    from django.db import models
    
    # Create your models here.
    class Destinations(models.Model):
        author = models.CharField(max_length=200, unique=False)
        tour_title = models.CharField(max_length=250)
        description = models.TextField()
        location = models.CharField(max_length=250)
        booking_start_date = models.DateField()
        booking_end_date = models.DateField()
        price = models.DecimalField(max_digits=6, decimal_places=2)
    
        def __str__(self):
            return str(self.tour_title)
    
    
    class Comment(models.Model):
        post = models.ForeignKey(Destinations,
           on_delete=models.CASCADE,related_name='comments')
        name = models.CharField(max_length=80)
        email = models.EmailField()
        comment = models.TextField()
        created_on = models.DateTimeField(auto_now_add=True)
        active = models.BooleanField(default=False)
    
        def __str__(self):
            return str(self.name)
    Python

    Depois de criar os modelos e fazer as migrations vamos testar para ver se os valores batem com os testes:

    python manage.py test
    Found 2 test(s).
    Creating test database for alias 'default'...
    System check identified no issues (0 silenced).
    ..
    --------------------------------------------------------------------
    Ran 2 tests in 0.005s
    
    OK
    Destroying test database for alias 'default'...
    Bash

    Criando testes para os endpoints da aplicação REST API

    Agora para testar os endpoints desta aplicação sera necessário criar a representação dos dados da mesma forma que foi efetuado para testar os modelos.

    E também a representação dos dados e URLs para testar o CRUD em cada endpoint (Destinations e Comments).

    Criando esse setup dentro da classe de testes, sera necessário criar funções que testam cada operação CRUD dos endpoints.

    https://wordpress.vps9144.panel.icontainer.cloud/django-rest-framework-construindo-apis-restful-com-django/

    No código abaixo foi criado quatro funções para cada endpoint usando os dados adicionados ao método Setup(), sendo assim, testando cada método de requisição Create, Read, Update e Delete:

    tourism/tests/test_api.py:

    # tourism/tests/test_api.py
    
    import json
    from rest_framework import status
    from django.utils import timezone
    from django.http import response
    from django.urls import reverse
    from tourism.models import Destinations, Comment
    from rest_framework.authtoken.models import Token
    from rest_framework.test import APIClient, APITestCase
    
    
    class TourismTestCase(APITestCase):
    
        def setUp(self):
            
            self.headerInfo = {'content-type': 'application/json'}
            
            # PASSO 1
    
            # Criação de objetos que serão 
            # utilizados nos testes dos endpoints.
    
            # Instance do objeto Destinations.
            self.destination = Destinations.objects.create(
                tour_title = 'test1',
                booking_start_date='2021-05-30',
                booking_end_date= '2022-05-30',
                price=2000,
                description='test1',
                location='somewhere',
                author='test1',
            )
            self.destination.save()
    
            # Instance do objeto Comment.
            self.comment = Comment.objects.create(
                #the relation between both models
                post = self.destination,
                name = 'John',
                email = 'any@email.com',
                comment = 'test1',
                created_on = timezone.now(),
                active = False,
            )
            self.comment.save()
    
            # PASSO 2
    
            # Criação de dados para serem usados nos testes.
            self.destination_data = {
                'tour_title' :'test1',
                'booking_start_date':'2021-05-30',
                'booking_end_date':'2022-05-30',
                'price':2000,
                'description':'test1',
                'location':'somewhere',
                'author':'test1'
            }
    
            self.comment_data = {
                'post':self.destination.id,
                'name':'John',
                'email':'any@email.com',
                'comment':'test1',
                'created_on':timezone.now(),
                'active':False,
            }
    
            # PASSO 3
    
            # Criação da representação dos endpoints
            # que serão utilizados nos testes.
    
            self.url_destination_list = reverse('tourism:destinations-list')
            self.url_destination_detail = reverse('tourism:destinations-detail',
                                            kwargs={'pk': self.destination.pk})
    
            self.url_comment_url_list = reverse('tourism:comments-list')
            self.url_comment_detail = reverse('tourism:comments-detail', 
                                            kwargs={'pk': self.comment.pk})
    
        # PASSO 4
        
        # Usar os dados criados no setUp() 
        # para criar as funções de teste.
    
        def test_get_destination(self):
            """GET method"""
            response = self.client.get(self.url_destination_list, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
    
        def test_create_destination(self):
            """ Test POST method for Destination endpoint"""
            response = self.client.post(
                self.url_destination_list, self.destination_data,
                format='json'
            )
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
    
        def test_update_destination(self):
            """ Test PUT method for Destination endpoint"""
            response = self.client.put(
                self.url_destination_detail, self.destination_data,
                format='json'
            )
            self.assertEqual(response.status_code, status.HTTP_200_OK)
    
        def test_delete_destination(self):
            """ Test DELETE method for Destination endpoint"""
            response = self.client.delete(
                self.url_destination_detail, format='json'
            )
            self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
    
        def test_get_comment(self):
            """GET method"""
            response = self.client.get(self.url_comment_url_list, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
    
        def test_create_comment(self):
            """ Test POST method for Comment endpoint"""
            response = self.client.post(
                self.url_comment_url_list, self.comment_data,
                format='json'
            )
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
    
        def test_update_comment(self):
            """ Test PUT method for Comment endpoint"""
            response = self.client.put(
                self.url_comment_detail, self.comment_data,
                format='json'
            )
            self.assertEqual(response.status_code, status.HTTP_200_OK)
    
        def test_delete_comment(self):
            """ Test DELETE method for Comment endpoint"""
            response = self.client.delete(
                self.url_comment_detail, format='json'
            )
            self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
    Python

    Seguindo os exemplos acima sabemos que ao rodar o teste o mesmo não passará, pois ainda não foi criado os endpoints de Destinations e Comments.

    Desta maneira foi criado as URLs do app e adicionado na URL principal:

    project_name/urls.py:

    # project_name/urls.py
    
    from django.urls import include, path
    from rest_framework import routers
    from tourism.api.viewsets import DestinationsViewset, CommentViewset
    
    router = routers.DefaultRouter()
    router.register(r"destinations", DestinationsViewset, basename="destinations")
    router.register(r"comments", CommentViewset, basename="comments")
    
    app_name = 'tourism'
    
    urlpatterns = [
        path('', include(router.urls)),
    ]    
    Python

    project_name/url.py

    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/', include('tourism.urls')), # <------------ a inserção de tourism.urls.
    ]
    Python

    Também sera criados os serializers e também os viewsets para que os dados sejam serializados pelo framework django-rest-framework e aceitem postagens e edições de dados em formato JSON.

    tourism/api/serializers.py

    from django.forms import modelformset_factory
    from rest_framework import serializers
    from tourism.models import Destinations, Comment
    
    class DestinationsSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Destinations
            fields = '__all__'
    
    
    class CommentSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Comment
            fields = '__all__'
    Python

    tourism/api/viewsets.py:

    from rest_framework import viewsets
    from .serializers import DestinationsSerializer, CommentSerializer
    from tourism.models import Destinations, Comment
    
    
    class DestinationsViewset(viewsets.ModelViewSet):
        queryset = Destinations.objects.all()
        serializer_class = DestinationsSerializer
    
    class CommentViewset(viewsets.ModelViewSet):
        queryset = Comment.objects.all()
        serializer_class = CommentSerializer
    Python

    Agora que os dados necessários foram adicionados para a serialização dos mesmos, os testes serão aceitos corretamente fazendo entender que os endpoints estão funcionando corretamente:

    python manage.py test
    Found 10 test(s).
    Creating test database for alias 'default'...
    System check identified no issues (0 silenced).
    ..........
    --------------------------------------------------------------------
    Ran 10 tests in 0.061s
    
    OK
    Destroying test database for alias 'default'...
    Bash

    Voce pode ter acesso ao codigo deste artigo aqui

  • Views e URLs no Django: Fundamentos Para Gerenciar Rotas

    Views e URLs no Django: Fundamentos Para Gerenciar Rotas

    Django é um framework popular que ajuda desenvolvedores a criar aplicações web rapidamente e de maneira eficiente. Ele segue a arquitetura Model-View-Template (MVT), onde as views têm um papel essencial na entrega de conteúdo dinâmico aos usuários.

    Além das views, o roteamento de URLs no Django é o mecanismo que mapeia solicitações de URLs para as views corretas, garantindo que o usuário acesse o conteúdo desejado com facilidade.

    Neste post, vamos explorar em profundidade como views e roteamento de URLs no Django funcionam, destacando seus aspectos fundamentais e apresentando exemplos práticos para ajudar no desenvolvimento de páginas web dinâmicas e eficientes.

    O que são Views no Django

    No Django, as views são responsáveis por processar as solicitações HTTP que um servidor recebe e por retornar uma resposta apropriada. Essa resposta pode ser uma página HTML, um documento JSON, um arquivo ou qualquer outro tipo de dado que o cliente esteja esperando.

    Django mvt

    As views interagem com os modelos e templates, e basicamente controlam a lógica do que acontece quando um usuário acessa uma determinada URL.

    Existem dois tipos principais de views no Django:

    1. Views baseadas em função (Function-based views – FBVs).
    2. Views baseadas em classe (Class-based views – CBVs).

    Vamos explorar esses dois tipos em detalhe.

    Views baseadas em Função

    As views baseadas em função são simples funções Python que recebem um objeto request e retornam um objeto response. Elas são uma ótima escolha para projetos menores ou para views que não precisam de uma estrutura mais complexa.

    Um exemplo básico de view baseada em função:

    from django.http import HttpResponse
    
    def index(request):
        return HttpResponse("Bem-vindo à página inicial!")
    Python

    Aqui, temos uma função chamada index que recebe o objeto request e retorna uma resposta HTTP com o texto “Bem-vindo à página inicial!“. Essa view é simples e direta, o que torna o seu uso adequado para tarefas rápidas.

    https://wordpress.vps9144.panel.icontainer.cloud/introducao-ao-framework-django-compreendendo-o-framework-e-sua-arquitetura/#comments

    Views baseadas em Classe

    As views baseadas em classe oferecem uma abordagem mais estruturada e reutilizável para desenvolver views.

    Elas são especialmente úteis quando você precisa de mais flexibilidade ou quando a lógica da sua view é mais complexa.

    As views baseadas em classe no Django herdam de classes genéricas, o que facilita muito a implementação de funcionalidades comuns como exibição de listas, formulários e operações CRUD (Create, Read, Update, Delete).

    Exemplo de uma view baseada em classe:

    from django.views import View
    from django.http import HttpResponse
    
    class IndexView(View):
        def get(self, request):
            return HttpResponse("Bem-vindo à página inicial!")
    Python

    Nesse caso, a classe IndexView herda da classe base View, e implementamos o método get para processar as solicitações do tipo GET.

    O que é o Roteamento de URLs no Django

    O roteamento de URLs no Django é o mecanismo que conecta as URLs às suas respectivas views. O Django utiliza um sistema de mapeamento simples e eficiente, que permite direcionar URLs para as views corretas sem a necessidade de configurá-las manualmente para cada endpoint.

    Para definir as rotas no Django, utilizamos o arquivo urls.py, onde as URLs são associadas a views específicas. O roteamento de URLs no Django é simples, mas extremamente poderoso, permitindo também o uso de parâmetros dinâmicos nas URLs.

    Configurando URLs no Django

    Cada aplicativo no Django geralmente contém um arquivo urls.py, que define suas rotas. Além disso, o projeto principal também tem um arquivo urls.py para gerenciar as rotas globais. Aqui está um exemplo básico de como configurar o roteamento de URLs no Django:

    from django.urls import path
    from .views import index
    
    urlpatterns = [
        path('', index, name='index'),  # <-- Mapeia a URL raiz para a view index
    ]
    Python

    Neste exemplo, a URL raiz ('/') está associada à view index. Quando um usuário acessa o domínio principal, a função index será chamada e processará a solicitação.

    URLs com Parâmetros

    O roteamento de URLs no Django também permite a criação de rotas dinâmicas, onde parâmetros são passados pela URL para serem usados na view correspondente. Esse recurso é muito útil para construir URLs como '/perfil/usuario123/‘, onde o usuario123 é um parâmetro dinâmico.

    Exemplo de URL com parâmetro:

    from django.urls import path
    from .views import saudacao
    
    def saudacao(request, nome):
        return HttpResponse(f"Olá, {nome}!")
    
    urlpatterns = [
        path('saudacao//', saudacao, name='saudacao'),  # <-- Passa 'nome' como parâmetro
    ]
    
    Python

    Nesse caso, o Django captura o valor na URL e passa como argumento para a função saudacao. Quando a URL /saudacao/Joao/ é acessada, o Django chamará a função view e passará o valor Joao para a variável nome.

    Trabalhando com Templates e Views

    As views no Django também são responsáveis por renderizar templates, que são arquivos HTML que exibem informações dinâmicas aos usuários. A renderização de templates separa a lógica da aplicação da apresentação, garantindo um código mais organizado e de fácil manutenção.

    Exemplo de View com Template

    Abaixo, um exemplo de uma view que renderiza um template HTML usando o atalho render():

    from django.shortcuts import render
    
    def index(request):
        contexto = {'mensagem': 'Bem-vindo ao Django! :D'}
        return render(request, 'index.html', contexto)
    Python

    Neste exemplo, a função index renderiza o template index.html e passa um dicionário chamado contexto para o template. No template, você pode usar a variável mensagem para exibir o texto dinamicamente.

    Django

    Criando Templates Simples

    Os templates do Django são arquivos HTML que contêm tags específicas para lidar com variáveis e controle de fluxo, como laços e condicionais. Aqui está um exemplo básico de template:

    DOCTYPE html>
    <html lang="pt-br">
    <head>
        <meta charset="UTF-8">
        <title>Página Inicialtitle>
    head>
    <body>
        <h1>{{ mensagem }}h1>
    body>
    html>
    HTML

    Quando a view index renderiza este template, a variável mensagem será substituída pelo valor passado no contexto, resultando em algo assim na página web:

    Bem-vindo ao Django! 😀

    Views Genéricas no Django

    Além das views baseadas em função e classe, o Django oferece uma série de views genéricas que automatizam muitas das tarefas comuns ao desenvolver aplicações web. Essas views genéricas cobrem casos de uso como exibição de listas de objetos, detalhes de um objeto, criação, edição e exclusão.

    ListView e DetailView

    Duas das views genéricas mais utilizadas são ListView e DetailView. Elas ajudam a listar e exibir os detalhes de objetos sem precisar escrever toda a lógica de exibição do zero.

    Aqui está um exemplo de uma ListView para listar posts de um blog:

    from django.views.generic import ListView
    from .models import Post
    
    class PostListView(ListView):
        model = Post
        template_name = 'post_list.html'
        context_object_name = 'posts'
    Python

    Neste exemplo, PostListView automaticamente buscará todos os objetos do modelo Post e os enviará ao template post_list.html sob o nome de contexto posts.

    FormView

    O Django também fornece views genéricas para lidar com formulários. A FormView, por exemplo, facilita o processo de exibição e validação de formulários sem que você precise codificar manualmente todo o processo.

    Exemplo de FormView:

    from django.views.generic.edit import FormView
    from .forms import ContatoForm
    
    class ContatoFormView(FormView):
        template_name = 'contato.html'
        form_class = ContatoForm
        success_url = '/obrigado/'
    
        def form_valid(self, form):
            # Processa o formulário
            return super().form_valid(form)
    Python

    Aqui, a ContatoFormView exibe o formulário definido em ContatoForm, valida os dados submetidos e, se tudo estiver correto, redireciona para uma página de sucesso.

    Melhores Práticas no Uso de Views e Roteamento de URLs no Django

    Ao desenvolver com Django, é importante seguir algumas melhores práticas para garantir que suas views e URLs sejam eficientes, escaláveis e fáceis de manter. Algumas dicas incluem:

    1. Organização de URLs: Divida suas URLs em vários arquivos urls.py dentro dos aplicativos para evitar que o arquivo principal se torne muito grande e confuso.
    2. Uso de Namespaces: Utilize namespaces para agrupar as rotas de diferentes aplicativos. Isso ajuda a evitar conflitos de nomes de URLs.
    3. Classes View para Código Reutilizável: Sempre que possível, prefira views baseadas em classe para código mais organizado e reutilizável.
    4. Atenção ao Desempenho: Em views que fazem muitas consultas ao banco de dados, utilize o método select_related() ou prefetch_related() para otimizar as queries e reduzir o número de acessos ao banco.

    Conclusão

    Entender views e roteamento de URLs no Django é fundamental para o desenvolvimento de páginas dinâmicas e eficientes.

    Enquanto as views controlam a lógica de como as informações são processadas e exibidas. O roteamento de URLs garante que as solicitações do usuário sejam corretamente direcionadas para as views corretas.

    Ao dominar essas duas áreas, você estará preparado para desenvolver aplicações web robustas e escaláveis com Django, utilizando os melhores recursos que o framework tem a oferecer.

  • Interface De Administração Do Django

    Interface De Administração Do Django

    O Django é um dos frameworks mais populares e completos para desenvolvimento web em Python. Criado para facilitar a construção de aplicações web complexas com menos código e maior eficiência, ele segue o padrão “Model-View-Template” (MVT), o que facilita a separação lógica entre os dados, a interface de usuário e o processamento de regras de negócio.

    Uma das funcionalidades mais marcantes do Django é sua interface de administração que ja vem por padrão, conhecida como Django Admin.

    Desde a criação de um novo projeto, o Django fornece uma interface de administração completamente funcional e altamente personalizável. O que permite gerenciar com facilidade o conteúdo da aplicação sem precisar desenvolver um painel de controle do zero.

    O Django Admin tem sido um dos recursos favoritos de desenvolvedores e equipes de conteúdo, pois oferece uma maneira rápida de gerenciar modelos, usuários e permissões.

    Ele é projetado para ser usado tanto por desenvolvedores quanto por administradores de conteúdo, eliminando a necessidade de interfaces administrativas complexas ou construídas do zero.

    Neste post, você irá entender como configurar e usar o Django Admin para gerenciar o conteúdo da sua aplicação de maneira eficiente e profissional.

    Configuração Inicial do Projeto

    Antes de começar a utilizar o Django Admin, é necessário realizar algumas configurações básicas. Como a instalação do projeto e o app, para em seguida iniciarmos os proximos passos.

    Criando Um Ambiente Virtual

    Antes de tudo vamos iniciar a criação de um projeto django, é necessario a criação de um ambiente virtual python para isolarmos nossa aplicação.

    python3 -m venv venv
    Bash

    Criação De Um Projeto Django

    Adicione os comandos a seguir em sequência:

    # 1. cria o projeto
    django-admin startproject meu_projeto . 
    
    # 2. cria o app
    django-admin startapp postagens 
    Bash

    Conexão Do App De Postagens Com o Projeto

    Agora que o projeto e o aplicativo de postagens foram criados, podemos fazer a conexão do aplicativo no projeto:

    Em settings.py adicone o nome do aplicativo no seguinte lugar:

    INSTALLED_APPS = [
        # Outros aplicativos
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'postagens', # <---------------------- Aqui
    ]
    Python

    Criando Um Super Usuario

    Depois de conectar o aplicationvo no projeto, usando o settings.py, é hora de acessar a interface de administração, criando um super-usuário. No qual é um usuário com permissões completas sobre o sistema:

    python manage.py createsuperuser
    Bash

    Siga as instruções fornecidas no terminal para definir o nome de usuário, e-mail e senha. Uma vez concluído, o seu superusuário estará criado e pronto para acessar o site de administrador do django.

    Acessando a Interface De Administração (django admin)

    Após isso, já podemos iniciar o servidor de desenvolvimento para garantir que tudo está funcionando corretamente:

    python manage.py runserver
    Bash

    Com o servidor rodando, acesse a interface de administração navegando até a URL padrão:

    http://127.0.0.1:8000/admin/
    Bash

    Você verá uma tela de login, onde poderá inserir as credenciais do superusuário que acabou de criar.

    Admin Login Site

    Ao entrar, você será levado à página inicial da interface administrativa do Django, onde você poderá visualizar modelos registrados, gerênciar usuários e realizar outras tarefas administrativas.

    Registrando Modelos No Django Admin

    Porém como visto na imagem acima, podemos perceber que temos apenas os modelos padrões do proprio Django.

    Com isso, Agora que você já tem a interface do Django Admin funcionando, é hora de adicionar seus modelos para que eles possam ser gerenciados através dessa interface.

    O Que São Modelos No Django?

    No Django, os modelos representam as tabelas do banco de dados. Cada classe de modelo que você define em seu código Python é mapeada para uma tabela no banco de dados. Esses modelos contêm campos que representam colunas na tabela, como nome, e-mail, data de criação, entre outros.

    Registrando Modelos No Django Admin

    Por padrão, o Django Admin não exibe todos os modelos automaticamente. Você precisa registrar os modelos que deseja gerenciar na interface administrativa. Para isso, vá até o arquivo admin.py no seu aplicativo (caso não exista, crie o arquivo). Suponha que você tenha um modelo de Post de blog, como este:

    # postagens/models.py
    
    from django.db import models
    
    class Post(models.Model):
        titulo = models.CharField(max_length=200)
        conteudo = models.TextField()
        publicado_em = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return self.titulo
    Python

    Para registrar este modelo no Django Admin, edite o arquivo admin.py e adicione o seguinte código:

    # postagens/admin.py
    
    from django.contrib import admin
    from .models import Post # <--------- Não se esqueça do import
    
    admin.site.register(Post) # <----- Registro do model no site admin
    Python

    Após isso, ao voltar à interface administrativa e atualizar a página, você verá o modelo Post disponível para gerenciamento.

    Django Admin With Posts Image

    Customização Básica Do Django Admin

    É possível personalizar como os dados aparecem na interface administrativa. No Django Admin, você pode modificar como os campos do modelo são exibidos na lista de objetos ou no formulário de edição.

    Adicionando alguns dados (Posts) vamos dar uma olhada de como as postagens aparecem por padrão:

    1 – Criação de postagem

    2 – Depois da criação das postagens, desta form que ficará a listagem dos mesmos (padrão):

    Lembrando que os dados estão listados assim por causa do metodo __str__ que adicionamos la no model 😉

    Agora vamos adicionar uma nova classe chamada PostAdmin para modificar a maneira que listamos as postagens no site admin do django:

    # postagens/admin.py
    
    from django.contrib import admin
    from .model import Post
    
    class PostAdmin(admin.ModelAdmin):
        list_display = ('titulo', 'publicado_em')
        search_fields = ('titulo',)
    
    admin.site.register(Post, PostAdmin)
    Python

    Aqui, list_display define os campos que serão exibidos na lista de itens, e search_fields permite a criação de uma barra de pesquisa para filtrar os resultados pelo campo título.

    Assim que você adicionar e salvar essas mudanças, recarregue a pagina para ver as mudanças no admin site:

    Customizando a Interface De Edição De Postagens no Django Admin

    A verdadeira força do Django Admin está na sua extensibilidade. Com a classe ModelAdmin, você pode customizar praticamente tudo na interface administrativa para se adequar às suas necessidades.

    Organizando Campos No Formulário

    A interface de edição padrão do Django Admin exibe todos os campos de um modelo em um layout vertical simples. No entanto, você pode organizá-los de maneira mais estruturada. Usando o atributo fieldsets, é possível agrupar campos e adicionar títulos:

    # postagens/admin.py
    
    from django.contrib import admin
    from .models import Post
    
    # Register your models here.
    class PostAdmin(admin.ModelAdmin):
        fieldsets = ( # <------------------- Novo campo
            ('Informações Básicas', {
                'fields': ('titulo',)
            }),
            ('Conteúdo', {
                'fields': ('conteudo',)
            }),
        )
        list_display = ('titulo', 'publicado_em')
        search_fields = ('titulo',)
    
    admin.site.register(Post, PostAdmin)
    Python

    Antes de adicionar esses valores no seu codigo, entre nos detalhes de uma postagem e veja como os campos estao por padrão:

    Agora, adcione o codigo acima com o novo campo e observe as mudanças:

    Como voce pode ver, cada campo teve a adição de um cabeçalho explicando seu proposito.

    Criando Ações Customizadas

    Além de gerenciar os dados exibidos, você também pode definir ações customizadas que podem ser aplicadas em lote. Por exemplo, para criar uma ação que marca vários posts como despublicados:

    Adicionando Um Novo Campo No Model Post

    Nesse caso, na model de postagens eu irei adicionar um novo campo chamado publicado que pode ser verdadeiro ou falso (BoooleanField(default=True)):

    # postagens/models.py
    
    from django.db import models
    
    class Post(models.Model):
        titulo = models.CharField(max_length=200)
        conteudo = models.TextField()
        publicado_em = models.DateTimeField(auto_now_add=True)
        publicado = models.BooleanField(default=True) # <-------- Noco campo
    
        def __str__(self):
            return self.titulo
    Python

    Criando As Migrations e Refletindo No Banco De Dados

    Assim que este codigo for adicionado, será necessario fazer os comandos para criar um novo arquivo de migration dentro da aplicação e outro comando para refletir este novo arquivo no banco de dados:

    # Comando para criar arquivo de migrations
    python manage.py makemigrations
    
    # Comando para refletir a migration no banco de dados.
    python manage.py migrate
    Bash

    Assim que esses comandos forem efetuados, voce estara pronto/apto para efetuar tambem as mudanças no site admin (no mesmo arquivo e a mesma classe que estavamos trabalhando antes).

    Adicionando Um Novo Campo e Criando Nova Ação Customizada

    # postagens/admin.py
    
    from django.contrib import admin
    from .models import Post
    
    def despublicar_post(modeladmin, request, queryset): <--- Nova função
        """
        Muda o status de uma 
        postagem para despublicado.
        """
        queryset.update(publicado=False)
    
    # Register your models here.
    class PostAdmin(admin.ModelAdmin):
        actions = [despublicar_post] <--- Ação adicionada com o nome da função criada acima.
        
        fieldsets = ( 
            ('Informações Básicas', {
                'fields': ('titulo',)
            }),
            ('Conteúdo', {
                'fields': ('conteudo',)
            }),
            ('Estatus', { <--- Novo campo adicionado para listagem
                'fields': ('publicado',)
            }),
        )
        list_display = ('titulo', 'publicado_em', 'publicado')
        search_fields = ('titulo',)
    
    despublicar_post.short_description = 'Marcar posts como despublicados'
    
    admin.site.register(Post, PostAdmin)
    Python

    Desta forma na listagem de postagens sera mostrado um novo campo detalhando se a postagem esta publicada ou não, e tambem o poder de despublicar varias postagens de uma vez.

    Checando as mudanças

    Neste caso eu irei apenas despublicar a primeira postagem para deixar o resultado como exemplo abaixo:

    Como pode ver, a postagem foi despublicada. Neste caso, ao clicar no “dropdown” e escolhido a ação de desplublicar, a função despublicar_post() foi acionada mudando o estatus desta postagem. Porem, caso necessite efetuar a mundaçã de estatus (deixar o mesmo como publicado).

    Simplesmente, clique na postagem no qual efetuamos o estatus de publicado anteriormente e clique para que o mesmo fique com o estatus de publicado novamente.

    Aproveite o código desta aplicação e continue com este projeto

    Conclusão

    O Django Admin é uma das funcionalidades mais convenientes e poderosas para quem desenvolve aplicações com o Django.

    Lembrando que essas mudanças no site admin são apenas algumas que e possivel efetuar. Existe uma gama enorme de possibilidades que podemos aplicar alem dessas.

    Sendo assim, ao entender como configurar e customizar a interface de administração, é possível gerenciar o conteúdo de maneira eficiente e segura.

    Se usado corretamente, ele elimina a necessidade de construir um painel administrativo do zero, poupando tempo e recursos.