Cansado(a) de quebrar a cabeça para aprender a programar Python de verdade?
Conheça a melhor e mais completa formação de Python e Django e sinta-se
um programador verdadeiramente competente. Além de Python e Django, você também vai aprender Banco de Dados, SQL,
HTML, CSS, Javascript, Bootstrap e muito mais!
Projetos práticos voltados para o mercado de trabalho
Formação moderna com foco na prática profissional
✅ Artigo atualizado em Setembro 2026
Versões atualizadas para DRF 3.18 e Django 6.1 (com a alternativa 5.2 LTS). O projeto do guia (models, serializers, ViewSets, routers, filtros, paginação, versionamento, token, JWT, drf-spectacular e a suíte de testes) foi executado com Django 6.1.1 e DRF 3.18.1. Corrigimos o CategoryViewSet que faltava, uma validação que quebrava em PATCH, um exemplo de permissão que deixava o PATCH aberto e uma configuração de segurança removida no Django 4.0.
Salve salve Pythonista!
Django REST Framework (DRF) é o padrão de facto para construir APIs REST profissionais com Python e Django.
Na versão atual (3.18, de setembro de 2026, compatível com Django 5.2 LTS, 6.0 e 6.1), o DRF continua sendo uma das escolhas mais populares para quem precisa criar APIs robustas, escaláveis e bem documentadas com Django.
Este é o guia definitivo de Django REST Framework: um artigo pilar que cobre desde a configuração inicial até técnicas avançadas de autenticação, filtros, paginação, versionamento e testes.
Se você quer dominar a criação de APIs REST profissionais com Django, este guia é para você! 🚀
pip install"django==6.1.*""djangorestframework==3.18.*"
pip install markdown django-filter # Opcionais mas úteis
O Django 6.1 (versão estável atual) exige Python 3.12 ou superior. Se você está no Python 3.10 ou 3.11, use o Django 5.2 LTS (pip install "django==5.2.*"), que tem suporte de segurança até abril de 2028 e também é suportado pelo DRF 3.18.
Criar Projeto Django
1
2
3
django-admin startproject myapi
cd myapi
python manage.py startapp products
# products/serializers.py
fromrest_frameworkimportserializersfrom.modelsimportProduct,CategoryclassCategorySerializer(serializers.ModelSerializer):products_count=serializers.SerializerMethodField()classMeta:model=Categoryfields=['id','name','slug','created_at','products_count']read_only_fields=['id','created_at']defget_products_count(self,obj)->int:returnobj.products.count()classProductSerializer(serializers.ModelSerializer):category_name=serializers.CharField(source='category.name',read_only=True)created_by_username=serializers.CharField(source='created_by.username',read_only=True)classMeta:model=Productfields=['id','name','slug','description','price','stock','category','category_name','status','created_by','created_by_username','created_at','updated_at']read_only_fields=['id','created_at','updated_at','created_by']defvalidate_price(self,value):"""Validação customizada"""ifvalue<=0:raiseserializers.ValidationError("Preço deve ser maior que zero")ifvalue>999999.99:raiseserializers.ValidationError("Preço muito alto")returnvaluedefvalidate(self,data):"""Validação de múltiplos campos"""# Em PATCH (partial=True) os campos ausentes vêm da instância atual
status=data.get('status',getattr(self.instance,'status',None))stock=data.get('stock',getattr(self.instance,'stock',0))ifstatus=='published'andstock==0:raiseserializers.ValidationError("Não é possível publicar produto sem estoque")returndata
fromrest_framework.viewsimportAPIViewfromrest_framework.responseimportResponsefromrest_frameworkimportstatusclassProductListView(APIView):"""API para listar e criar produtos"""defget(self,request):products=Product.objects.select_related('category').all()serializer=ProductSerializer(products,many=True)returnResponse(serializer.data)defpost(self,request):serializer=ProductSerializer(data=request.data)ifserializer.is_valid():serializer.save(created_by=request.user)returnResponse(serializer.data,status=status.HTTP_201_CREATED)returnResponse(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
fromrest_frameworkimportstatus,viewsetsfromrest_framework.decoratorsimportactionfromrest_framework.responseimportResponsefrom.modelsimportCategory,Productfrom.serializersimportCategorySerializer,ProductDetailSerializer,ProductSerializerclassCategoryViewSet(viewsets.ModelViewSet):queryset=Category.objects.order_by('name')serializer_class=CategorySerializerlookup_field='slug'classProductViewSet(viewsets.ModelViewSet):"""
ViewSet completo para Product
list: GET /products/
create: POST /products/
retrieve: GET /products/{id}/
update: PUT /products/{id}/
partial_update: PATCH /products/{id}/
destroy: DELETE /products/{id}/
"""queryset=Product.objects.select_related('category','created_by')serializer_class=ProductSerializerlookup_field='slug'filterset_fields=['status','category']search_fields=['name','description']ordering_fields=['price','created_at','stock']defget_serializer_class(self):"""Usar serializer diferente para detail"""ifself.action=='retrieve':returnProductDetailSerializerreturnProductSerializerdefperform_create(self,serializer):serializer.save(created_by=self.request.user)@action(detail=True,methods=['post'])defpublish(self,request,slug=None):"""Custom action: /products/{slug}/publish/"""product=self.get_object()ifproduct.stock==0:returnResponse({'error':'Produto sem estoque'},status=status.HTTP_400_BAD_REQUEST)product.status='published'product.save()serializer=self.get_serializer(product)returnResponse(serializer.data)@action(detail=False,methods=['get'])deflow_stock(self,request):"""Custom action: /products/low_stock/"""products=self.get_queryset().filter(stock__lt=10,status='published')serializer=self.get_serializer(products,many=True)returnResponse(serializer.data)
# Criar token automaticamente para novos usuários
fromdjango.confimportsettingsfromdjango.db.models.signalsimportpost_savefromdjango.dispatchimportreceiverfromrest_framework.authtoken.modelsimportToken@receiver(post_save,sender=settings.AUTH_USER_MODEL)defcreate_auth_token(sender,instance=None,created=False,**kwargs):ifcreated:Token.objects.create(user=instance)
Autenticação: JWT
1
pip install djangorestframework-simplejwt
Testamos a obtenção e a validação de tokens com djangorestframework-simplejwt 5.5.1 no Django 6.1.1 e DRF 3.18.1. Oficialmente, essa versão do pacote declara suporte até o Django 5.2; confira as notas de versão do simplejwt antes de atualizar em produção.
# products/permissions.py
fromrest_frameworkimportpermissionsclassIsOwnerOrReadOnly(permissions.BasePermission):"""Apenas dono pode editar"""defhas_object_permission(self,request,view,obj):# Leitura permitida para todos
ifrequest.methodinpermissions.SAFE_METHODS:returnTrue# Escrita apenas para dono
returnobj.created_by==request.userclassIsAdminOrReadOnly(permissions.BasePermission):"""Apenas admin pode criar/editar/deletar"""defhas_permission(self,request,view):ifrequest.methodinpermissions.SAFE_METHODS:returnTruereturnrequest.userandrequest.user.is_staff
1
2
3
4
5
# Usar nas views
classProductViewSet(viewsets.ModelViewSet):permission_classes=[IsOwnerOrReadOnly]# ou
permission_classes=[IsAdminOrReadOnly]
Filtros e paginação são só a cereja do bolo - a base que sustenta tudo isso é Django bem aprendido, de model a view. É isso que você constrói na Jornada Python:
Domine Python: A Linguagem da Inteligência Artificial, do
Desenvolvimento Web e das Carreiras do Futuro.
A mais completa formação para você se tornar um especialista em Python,
pronto para construir desde aplicações web robustas com Django até
projetos de Inteligência Artificial e Ciência de Dados.
Nota 4,9 de 5 na HotmartO curso de Python mais bem avaliado da Hotmart!
GET /api/products/?status=published
GET /api/products/?min_price=50&max_price=500
GET /api/products/?search=notebook
GET /api/products/?ordering=-price
GET /api/products/?category=1&status=published&ordering=price
# products/tests.py
fromrest_framework.testimportAPITestCase,APIClientfromrest_frameworkimportstatusfromdjango.contrib.auth.modelsimportUserfrom.modelsimportProduct,CategoryclassProductAPITestCase(APITestCase):defsetUp(self):"""Executado antes de cada teste"""self.client=APIClient()self.user=User.objects.create_user(username='testuser',password='testpass123')self.category=Category.objects.create(name='Eletrônicos',slug='eletronicos')self.product=Product.objects.create(name='Notebook',slug='notebook',description='Notebook de teste',price=3000.00,stock=10,category=self.category,created_by=self.user,status='published')deftest_list_products(self):"""Teste GET /api/products/"""response=self.client.get('/api/products/')self.assertEqual(response.status_code,status.HTTP_200_OK)self.assertEqual(len(response.data['results']),1)deftest_create_product_authenticated(self):"""Teste POST /api/products/ (autenticado)"""self.client.force_authenticate(user=self.user)data={'name':'Mouse','slug':'mouse','description':'Mouse gamer','price':150.00,'stock':50,'category':self.category.id,'status':'draft'}response=self.client.post('/api/products/',data,format='json')self.assertEqual(response.status_code,status.HTTP_201_CREATED)self.assertEqual(Product.objects.count(),2)deftest_create_product_unauthenticated(self):"""Teste POST sem autenticação"""data={'name':'Test'}response=self.client.post('/api/products/',data)self.assertEqual(response.status_code,status.HTTP_401_UNAUTHORIZED)deftest_update_product(self):"""Teste PATCH /api/products/{slug}/"""self.client.force_authenticate(user=self.user)data={'price':2500.00}response=self.client.patch(f'/api/products/{self.product.slug}/',data,format='json')self.assertEqual(response.status_code,status.HTTP_200_OK)self.product.refresh_from_db()self.assertEqual(float(self.product.price),2500.00)deftest_delete_product(self):"""Teste DELETE /api/products/{slug}/"""self.client.force_authenticate(user=self.user)response=self.client.delete(f'/api/products/{self.product.slug}/')self.assertEqual(response.status_code,status.HTTP_204_NO_CONTENT)self.assertEqual(Product.objects.count(),0)deftest_filter_by_status(self):"""Teste filtros"""response=self.client.get('/api/products/?status=published')self.assertEqual(response.status_code,status.HTTP_200_OK)self.assertEqual(len(response.data['results']),1)deftest_search(self):"""Teste busca"""response=self.client.get('/api/products/?search=Notebook')self.assertEqual(response.status_code,status.HTTP_200_OK)self.assertEqual(len(response.data['results']),1)
Executar Testes
1
2
3
4
5
6
7
8
9
10
11
# Todos os testes
python manage.py test# Testes específicos
python manage.py test products.tests.ProductAPITestCase
# Com coverage
pip install coverage
coverage run --source='.' manage.py test
coverage report
coverage html # Gera relatório HTML
11. Documentação Automática (OpenAPI/Swagger)
A geração de OpenAPI embutida no DRF está marcada como descontinuada na documentação oficial, que recomenda o drf-spectacular (o antigo suporte a CoreAPI foi removido no DRF 3.17). A configuração abaixo foi testada com drf-spectacular 0.30.0, Django 6.1.1 e DRF 3.18.1.
1
pip install drf-spectacular
1
2
3
4
5
6
7
8
9
10
11
12
13
# settings.py
INSTALLED_APPS+=['drf_spectacular']REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS':'drf_spectacular.openapi.AutoSchema',}SPECTACULAR_SETTINGS={'TITLE':'Products API','DESCRIPTION':'API REST para gerenciamento de produtos','VERSION':'1.0.0','SERVE_INCLUDE_SCHEMA':False,}
# settings.py
INSTALLED_APPS+=['corsheaders']MIDDLEWARE=['corsheaders.middleware.CorsMiddleware','django.middleware.common.CommonMiddleware',# ...
]# Desenvolvimento
CORS_ALLOWED_ORIGINS=["http://localhost:3000","http://localhost:8080",]# Produção (mais restritivo)
CORS_ALLOWED_ORIGINS=["https://meusite.com",]# Ou permitir tudo (NÃO recomendado em produção)
CORS_ALLOW_ALL_ORIGINS=True
SECRET_KEY=os.environ.get('SECRET_KEY')SECURE_SSL_REDIRECT=TrueSESSION_COOKIE_SECURE=TrueCSRF_COOKIE_SECURE=TrueSECURE_HSTS_SECONDS=31536000# HSTS: ative só depois de confirmar que todo o site responde em HTTPS
A antiga configuração SECURE_BROWSER_XSS_FILTER foi removida no Django 4.0 (os navegadores modernos ignoram o cabeçalho X-XSS-Protection). Para conferir o que falta na sua configuração de produção, rode python manage.py check --deploy.
Neste guia completo de Django REST Framework, você aprendeu:
✅ Por que usar DRF - Vantagens e comparações
✅ Instalação e setup - Configuração completa
✅ Models e Serializers - Validação e serialização
✅ Views e ViewSets - FBV, CBV, Generic, ViewSets
✅ Autenticação - Token, JWT, permissões customizadas
✅ Filtros e paginação - django-filter, search, ordering
✅ Testes - APITestCase completo
✅ Documentação - OpenAPI/Swagger
✅ Produção - Deploy, segurança, boas práticas
Principais lições:
ViewSets são a forma mais eficiente de criar APIs CRUD
Serializers fazem validação automática de dados
Permissions garantem segurança granular
Filtros tornam a API flexível e poderosa
Testes são essenciais para APIs confiáveis
DRF é ideal para apps Django que precisam de APIs REST
Próximos passos:
Implemente autenticação JWT em seu projeto
Adicione testes com cobertura > 80%
Configure documentação Swagger
Otimize queries com select_related/prefetch_related
Deploy em produção com Docker + Gunicorn + Nginx
Django REST Framework é a escolha certa quando você precisa de APIs REST robustas, bem documentadas e escaláveis com Django!
Quer aprofundar ainda mais e construir APIs com serializers, viewsets, routers e autenticação dentro de um projeto Django completo? Conheça o curso de Django da Jornada Python.
Até a próxima! 🚀
Começe agora sua Jornada na Programação!
Não deixe para amanhã o sucesso que você pode começar a construir hoje!