Lista de Tarefas Django Python
Vamos lá! Primeiro você precisa instalar o Python, você pode gerenciar múltiplas versões usando o pyenv:
brew update
brew install pyenv
pyenv install 3.12.0
O Django é um popular framework web Python de código aberto. Foi lançado em 2005 e é mantido pela Django Software Foundation.
- Baseado no padrão model-view-controller (MVC). Isso ajuda a manter a lógica de negócios, a lógica de apresentação e a lógica de processamento de dados separadas.
- Inclui um ORM (Object Relational Mapper) que permite abstrair e mapear facilmente objetos do Python para bancos de dados.
- Oferece um sistema de templates poderoso baseado em sintaxe simples que permite a separação entre lógica e apresentação.
- Inclui um sistema de autenticação de usuários e gerenciamento de sessão pronto para uso.
- Administração automática do site disponível pronta para uso para gerenciar facilmente conteúdo e usuários.
- Grande comunidade, muitos pacotes de terceiros e boa documentação disponível. Para instalar o Django, você pode seguir a documentação.
> python -m pip install Django
> python -m django --version
4.2.7
O Django Rest Framework (DRF) é um pacote popular que se integra ao Django para facilitar a construção de APIs.
- O DRF é construído sobre o Django e se aproveita de muitos de seus recursos, como o sistema de autenticação e o ORM.
- Ele permite rapidamente criar APIs web escaláveis utilizando conceitos do Django como models e views.
- Adiciona recursos específicos para construção de APIs, como serialização de dados, controle de requisições HTTP, paginação e limitação de requisições.
- Usa classes baseadas em views genéricas para lidar com requisições comuns de API como lista, detalhe, criação, exclusão e atualização de dados.
- Fornece serializadores que traduzem entre representações complexas em Python e representações nativas da web como JSON.
- Inclui ferramentas de autenticação e permissão específicas para APIs.
Para gerenciar os pacotes de nossa aplicação vamos usar o gerenciador de pacotes chamado Poetry.
Agora vamos começar nosso projeto
mkdir todo_list
cd todo_list
Vamos inicializar o Poetry:
poetry init -n
E adicionar o Django, o DRF e um pacote para trabalharmos com JWT:
poetry add django djangorestframework djangorestframework-simplejwt
Agora vamos ativar o ambiente shell do Poetry:
poetry shell
E criamos nosso projeto:
django-admin startproject todo_project .
No Django podemos ter diferentes apps/módulos e por isso vamos criar nossos apps de tarefas (todos) e de gestão de usuários (users)
mkdir todo_project/auth
python manage.py startapp todos
python manage.py startapp users
Adicione os apps e rest_framework ao array INSTALLED_APPS em settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', #ADDED
'auth', #ADDED
'users', #ADDED
]
Nesse passo vamos efetuar as migrations
poetry run python manage.py migrate
Você verá que um banco de dados sqlite será criado na raiz do projeto.
E pode rodar a aplicação pelo comando:
python manage.py runserver
Agora você pode acessar a aplicação: http://127.0.0.1:8000
Você também pode acessar o painel administrativo, mas antes é preciso criar um superuser:
poetry run python manage.py createsuperuser
APP USERS
Primeiramente, vamos criar um model de User customizado para caso necessário expandirmos as propriedades defaults. O arquivo models.py ficará assim:
from django.contrib.auth.models import AbstractUser
from .managers import CustomUserManager
from django.contrib.auth.models import UserManager
class CustomUserManager(UserManager):
pass;
class CustomUser(AbstractUser):
objects = CustomUserManager()
def __str__(self):
return f"Username: {self.username} <Email: {self.email}>"
Teremos dois serializers, um para criar o user e outro para mudar a senha do user, portanto, crie o arquivo serializers.py:
from rest_framework import serializers
from .models import CustomUser
class RegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={"input_type": "password"}, write_only=True)
class Meta:
model = CustomUser
fields = ['username','email', 'password', 'password2']
extra_kwargs = {
'password': {'write_only': True}
}
def save(self):
user = CustomUser(username=self.validated_data['username'],email=self.validated_data['email'])
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError({'password': 'Passwords must match.'})
user.set_password(password)
user.save()
return user
class PasswordChangeSerializer(serializers.Serializer):
current_password = serializers.CharField(style={"input_type": "password"}, required=True)
new_password = serializers.CharField(style={"input_type": "password"}, required=True)
def validate_current_password(self, value):
if not self.context['request'].user.check_password(value):
raise serializers.ValidationError({'current_password': 'Does not match'})
return value
Nas Views da aplicação user vamos criar nosso CRUD de usuário:
from django.contrib.auth import authenticate, login, logout
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from .serializers import RegistrationSerializer, PasswordChangeSerializer
from rest_framework_simplejwt.tokens import RefreshToken
def get_tokens_for_user(user):
refresh = RefreshToken.for_user(user)
return {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
class RegistrationView(APIView):
def post(self, request):
serializer = RegistrationSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class LoginView(APIView):
def post(self, request):
if 'username' not in request.data or 'password' not in request.data:
return Response({'msg': 'Credentials missing'}, status=status.HTTP_400_BAD_REQUEST)
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
auth_data = get_tokens_for_user(request.user)
return Response({'msg': 'Login Success', **auth_data}, status=status.HTTP_200_OK)
return Response({'msg': 'Invalid Credentials'}, status=status.HTTP_401_UNAUTHORIZED)
class LogoutView(APIView):
def post(self, request):
logout(request)
return Response({'msg': 'Successfully Logged out'}, status=status.HTTP_200_OK)
class ProfileView(APIView):
permission_classes = [IsAuthenticated, ]
def get(self, request):
user = request.user
data = {
'id': user.id,
'username': user.username,
'first_name': user.first_name,
'last_name': user.last_name,
'email': user.email
}
return Response(data, status=status.HTTP_200_OK)
class ChangePasswordView(APIView):
permission_classes = [IsAuthenticated, ]
def post(self, request):
serializer = PasswordChangeSerializer(context={'request': request}, data=request.data)
serializer.is_valid(raise_exception=True)
request.user.set_password(serializer.validated_data['new_password'])
request.user.save()
return Response(status=status.HTTP_204_NO_CONTENT)
Agora vamos expor essas views nas URL:
from django.urls import path
from .views import RegistrationView, LoginView, LogoutView, ChangePasswordView, ProfileView
from rest_framework_simplejwt import views as jwt_views
app_name = 'users'
urlpatterns = [
path('register', RegistrationView.as_view(), name='register'),
path('login', LoginView.as_view(), name='login'),
path('me', ProfileView.as_view(), name='profile'),
path('logout', LogoutView.as_view(), name='logout'),
path('change-password', ChangePasswordView.as_view(), name='password'),
path('token-refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'),
]
Também vamos adicionar algumas configurações de autenticação ao arquivo settings.py da pasta todo_project:
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny'
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': datetime.timedelta(days=1),
'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=1),
}
AUTH_PROFILE_MODULE = 'users.CustomUser'
AUTH_USER_MODEL = 'users.CustomUser'
APP TODOS
Crie o model para os todos no arquivo models.py do módulo todos:
from django.db import models
class Todo(models.Model):
title = models.TextField()
description = models.TextField(null=True, blank=True)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
is_completed = models.BooleanField(default=False)
owner = models.ForeignKey(
'users.CustomUser',
related_name='todos',
on_delete=models.CASCADE,
)
def __str__(self):
return f"Title: {self.title} <Completed: {self.is_completed}>"
Crie o arquivo serializers.py:
from rest_framework import serializers
from .models import Todo
class TodoSerializer(serializers.ModelSerializer):
class Meta:
fields = (
'id',
'title',
'description',
'is_completed',
'owner',
)
model = Todo
E agora vamos fazer as views de CRUD do todo:
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from .models import Todo
from .serializers import TodoSerializer
class TodoViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
serializer_class = TodoSerializer
def get_queryset(self):
return Todo.objects.filter(owner=self.request.user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def destroy(self, request, *args, **kwargs):
todo = self.get_object()
if todo.owner == request.user:
todo.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
def partial_update(self, request, *args, **kwargs):
todo = self.get_object()
if todo.owner != request.user:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = self.get_serializer(todo, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
Agora precisamos gerar e executar a migration do todo model:
poetry run python manage.py makemigrations
python manage.py migrate
Documentação
Vamos adicionar uma bibliotega para gerar nossa documentação automaticamente:
poetry add drf-yasg
Adicione ao arquivo settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'drf_yasg', # ADDED
'users',
'todos',
]
E nosso arquivo de urls.py ficará assim, com as rotas de documentação:
from django.contrib import admin
from django.urls import path, include
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="Todo API",
default_version='v1',
description="Simple todo API",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="me@abilioazevedo.com.br"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)
urlpatterns = [
path('swagger<format>/', schema_view.without_ui(cache_timeout=0), name='schema-json'),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
path('admin/', admin.site.urls),
path("api/v1/users/", include("users.urls"), name="users"),
path("api/v1/", include("todos.urls"), name="todos")
]
Agora é só acessar: http://127.0.0.1:8000/swagger/.
Deploy
Você pode usar o https://render.com/ para implantar seu aplicativo, você só precisa conectar seu repositório e implantá-lo usando o Docker. Acesse o render para criar seu aplicativo web: https://dashboard.render.com/create?type=web
Você pode testar a aplicação aqui
Comandos Extras
Para checar por migrations do banco de dados
poetry run python manage.py showmigrations
Se a migration já tiver sido efetuada, mas o Django não marcou como feita, você pode efetuar de maneira falsa:
poetry run python manage.py migrate --fake transactions 0009
Ir para uma migration específica
poetry run python manage.py migrate app_name 0003
Para imprimir alguma informação você pode usar a função:
print(obj)
Rodar testes:
poetry python manage.py test
Rodar a shell do python:
poetry python manage.py shell_plus
Dentro da shell podemos importar e executar arquivos:
from .models import Todo
todo = Todo(title='Create a todo list', description="Using Django")
todo.save()
print(Todo.objects.all())
Modelos proxy do Django
Modelos proxy do Django permitem modificar o comportamento de um modelo existente sem criar uma nova tabela no banco de dados. Pontos principais:
- Propósito: Adicionar métodos ou alterar o gerenciador padrão sem modificar o esquema do banco de dados.
- Uso: Herdar de um modelo existente e definir Meta.proxy = True.
- Limitações: Não pode adicionar novos campos ou alterar os existentes.
- Casos de uso:
- Adicionar métodos personalizados
- Modificar o gerenciador padrão
- Alterar metadados do modelo (por exemplo, ordenação padrão)
- Benefícios:
- Organização do código
- Personalização de comportamento sem alterações no banco de dados Exemplo:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=100)
class VIPPerson(Person):
class Meta:
proxy = True
def get_vip_status(self):
return "VIP"