Django Rest Framework (DRF)

O Django REST Framework é uma ferramenta (uma biblioteca do Django) que te ajuda a criar APIs de forma simples, organizada e segura. Ele pega tudo que o Django já faz de bom e adiciona funcionalidades específicas pra criar APIs REST

O Django REST Framework (DRF) faz a mesma coisa do django, mas: Já tem um monte de coisa pronta (serialização, validação, status HTTP, autenticação, etc), Deixa tudo mais organizado e reutilizável, Gera interfaces automáticas para testar as APIs, Facilita o uso de modelos com bancos de dados

REST é a sigla pra Representational State Transfer. É um estilo de arquitetura usado para construir APIs (interfaces de programação de aplicações).

Uma API REST funciona com requisições HTTP (como GET, POST, PUT, DELETE). Cada tipo serve pra uma coisa:

Método O que faz Exemplo
GET Buscar dados Listar todos os usuários
POST Criar dados Criar um novo usuário
PUT Atualizar dados Atualizar os dados de alguém
DELETE Apagar dados Remover um usuário

A API REST responde geralmente com JSON, que é um formato leve e fácil de ler.

serializers são como “tradutores” entre o banco de dados (models) e o JSON da API.
🔄 Converter    Transforma objetos Python (ou modelos Django) em JSON (ou outros formatos), e vice-versa.
✅ Validar    Garante que os dados recebidos da requisição sejam válidos antes de salvar no banco.
📦 Integrar    Funciona como uma "ponte" entre a API e o banco de dados (via models).

Dizemos que serializers transforma os objeto do banco (por exemplo) em Json, mas na verdade transforma em uma lista de dicionarios pronta para ser convertida facilmente em JSON pelo response.

Use validações simples e estruturais no Model (ex: unique=True, validators=[...]), e, use validações de entrada, lógica mais complexa ou mensagens mais amigáveis no Serializer.

Uma ViewSet é uma classe no Django REST Framework que automaticamente cria os endpoints da sua API REST. ajuda a criar uma API sem ter que escrever uma view pra cada coisa (listar, cadastrar, atualizar, deletar). É basicamente uma CBV de uma api.

Django "puro" Django REST Framework (DRF)
Function-Based View (FBV) APIView (base simples)
Class-Based View (CBV) ViewSet

DefaultRouter é uma classe do Django REST Framework que gera rotas automaticamente com base no nome do caminho e na view que é para chamar (geralmente viewSet) que é passado no register(). Se não usasse, teria que fazer manualmente cada rota.

Conceito O que faz
routes Caminhos da sua API
router Gera essas rotas automaticamente
DefaultRouter A versão mais usada, que cria rotas REST completas pra você
register() Conecta um caminho (produtos/) com uma ViewSet
urlpatterns Lista de todas as rotas ativas no seu projeto Django

 

A API Root no Django REST Framework (DRF) é tipo a "página inicial" da sua API — um ponto de entrada que lista todos os endpoints disponíveis da sua aplicação. Quando você usa o DefaultRouter do DRF, ele cria automaticamente uma view raiz, que é acessada geralmente no caminho / da sua API, organizada e com funções extras. sendo possivel ate fazer um post com um formulário html ja pronto.

No DRF, generics são atalhos prontos para criar views baseadas em classes (CBV) que fazem operações comuns em APIs como: listar dados, criar novos registros, editar, deletar. diferente das viewsets ela separa cada classe para uma função do CRUD. Isso garante mais controle.
Principais Generics:

Classe O que faz
ListAPIView Só permite GET (lista de objetos)
RetrieveAPIView GET de um único item (por ID)
CreateAPIView POST (criar novo item)
UpdateAPIView PUT/PATCH (editar item existente)
DestroyAPIView DELETE
ListCreateAPIView GET (listar) + POST (criar)
RetrieveUpdateDestroyAPIView GET + PUT + DELETE de um único item

Basic Authentication é um método em que o usuário e senha são enviados no cabeçalho da requisição HTTP, codificados em Base64. Não recomendado em produção sem HTTPS, pois a senha vai em texto codificado (não criptografado). Em produção, normalmente se usa Token Auth, JWT ou OAuth2

🛡️ Tabela de Permissões do DRF
Permissão (permission_classes) O que faz Quando usar
AllowAny Permite qualquer requisição, autenticada ou não. Para rotas públicas (ex: página inicial, catálogos abertos).
IsAuthenticated Só permite se o usuário estiver autenticado (logado). Para áreas restritas, como perfil, carrinho, histórico.
IsAdminUser Só permite usuários com is_staff=True (admin Django). Para painéis administrativos, cadastro de produtos, etc.
IsAuthenticatedOrReadOnly Qualquer um pode ver (GET), mas só autenticados podem alterar. Ex: comentários ou produtos públicos, mas alteração só com login.
DjangoModelPermissions Permite baseado nas permissões do modelo (add, change, delete). Quando quer usar o sistema de permissões do Django Admin.
DjangoObjectPermissions Igual ao acima, mas com base por objeto (mais avançado). Quando cada usuário pode alterar só os seus próprios objetos.
🔧 Permissão customizada Você pode criar sua própria classe de permissão Ex: somente donos do objeto podem alterar, ou lógica de idade, etc.

O versionamento de API no Django REST Framework (DRF) é uma prática essencial para manter compatibilidade entre diferentes versões da sua API — especialmente quando você faz mudanças que podem quebrar aplicações que dependem da versão antiga. A estratégia mais ultilizada e recomendada é a URLPathVersioning.

Estratégia de Versionamento:
Estratégia URL de exemplo
URLPathVersioning /api/v1/produtos/
QueryParameterVersioning /api/produtos/?version=v1
AcceptHeaderVersioning Accept: application/json; version=v1
NamespaceVersioning /api/v1/... com namespaces

Comandos DRF

bash
$ pip install django djangorestframework # instala o django e a biblioteca DRF juntos
$ pip install djangorestframework # instala apenas o DRF
$ pip show djangorestframework # Mostra a versão
settings.py
INSTALLED_APPS = [
    ...
    'rest_framework',
    'corsheaders' #permitir se comunicar com frontends externos, como o React via HTTP (CORS)
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware', # vem antes do commonMiddleware abaixo
    'django.middleware.common.CommonMiddleware',
    ...
]

REST_FRAMEWORK = {
    "COERCE_DECIMAL_TO_STRING": False,
    "DEFAULT_AUTHENTICATION_CLASSES": ( 'rest_frameework.renderers.JSONRenderer',), # desativa o browser api padrão do DRF, deixando apenas django puro
}

# Permite qualquer origem (cuidado em produção!)
CORS_ALLOW_ALL_ORIGINS = True

# Ou permite só de um lugar específico
# CORS_ALLOWED_ORIGINS = [
#     "http://localhost:3000",
#     "https://meusite.com",
# ]

O primeiro passo é configurar em settings.py o rest_framework em installed_apps.

COERCE_DECIMAL_TO_STRING= False, serve para informar ao Django REST Framework que, ao serializar dados para JSON, valores do tipo Decimal devem ser convertidos para número (float) e não para string

{
  "preco": "10.50"  ← é uma **string por padrão**
}

A biblioteca django-cors-headers (Biblioteca externa - instalar com pip) serve para permitir que o Django aceite requisições de outros domínios — algo que é bloqueado por padrão pelos navegadores por questão de segurança, chamado de CORS (Cross-Origin Resource Sharing).

Ela permite que seu frontend (ex: React) se comunique com seu backend (Django) mesmo estando em endereços diferentes (ex: localhost:3000 e localhost:8000).

| Exemplo simples Com e Sem DRF

views_sem_DRF.py
from django.http import JsonResponse

# Create your views here.

def participantes(request):
    if request.method == 'GET':
        participantes = {
            "id": 1,
            "nome": "Juan Perez",
        }
        return JsonResponse(participantes)
views_drf.py
from rest_framework.response import Response
from rest_framework.decorators import api_view

@api_view(['GET'])
def participantes(request):
    data = {"id": 1, "nome": "Juan Perez"}
    return Response(data)

Exemplo mais robusto com DRF.

| Exemplo Endpoint com DRF

Nesse projeto, leve em consideração que temos um modelo produto e um modelo categoria, que esta relacionado ao produto como chave estrangeira.

Crie um Arquivo serializers.py no seu app. O ReadOnlyField com source='categoria.nome' acessa o campo nome do modelo relacionado categoria.

serializers.py
from rest_framework import serializers
from .models import Produto, Cliente
# Uso minhas validações separadas
from .validators import nome_somente_letras

# Transforma objetos Produto em JSON (e vice-versa).
class ProdutoSerializer(serializers.ModelSerializer):
    # Mostrar o nome da categoria do produto, relacionado, mas só pra leitura
    categoria = serializers.ReadOnlyField(source='categoria.nome')
    # Valido nome com a classe criada em meu validators.py
    nome = serializers.CharField(validators=[nome_somente_letras])
    class Meta:
        model = Produto # modelo que irá serializar
        fields = ['id', 'nome', 'preco'] # Quais campos vão aparecer na API
        read_only_fields = ['id', 'data_criacao'] # Defino esses campos como somente leitura

class ClienteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Cliente
        fields = ['id', 'nome', 'email']

    # Cria um campo através de um método personalizado, sem adicionar ao model.
    site_pessoal = serializers.SerializerMethodField(
        method_name='any_method_name',
        read_only=True,
    )
    # método usado para gerar conteúdo para um campo
    def any_method_name(self, recipe):
        return 'www.conteudodocampo.com'

Nesse exemplo, leve em consideração que temos um modelo produto com nome e preço.

ProdutoSerializer:

  • Consegue transformar um objeto Produto em JSON

  • Consegue ler um JSON e criar/atualizar um objeto Produto

  • Já cuida da validação dos campos automaticamente com base no modelo, prefira sempre deixar validações, como de email e etc aqui.

validators.py
from rest_framework import serializers

def nome_somente_letras(valor):
    if not valor.replace(" ", "").isalpha():
        raise serializers.ValidationError("O nome do produto deve conter apenas letras.")
views_simples.py
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .models import Produto
from .serializers import ProdutoSerializer

@api_view(['GET'])
def lista_produtos(request):
    produtos = Produto.objects.all() # minha queryset
    # informa que é uma lista de objetos
    serializer = ProdutoSerializer(produtos, many=True)
    return Response(serializer.data)
views_viewSet_e_generics.py
from rest_framework import generics
from rest_framework.viewsets import ModelViewSet
from .models import Produto, Cliente
from .serializers import ProdutoSerializer, ClienteSerializer

# ViewSet para Produto (CRUD completo)
class ProdutoViewSet(ModelViewSet):
    queryset = Produto.objects.all()
    serializer_class = ProdutoSerializer

# Generics para Cliente (apenas List e Create)
class ClienteListCreateView(generics.ListCreateAPIView):
    queryset = Cliente.objects.all()
    serializer_class = ClienteSerializer

Exemplo com uma viewset. Essa classe sozinha já é capaz de lidar com (isso pode ser gerado pelo router):

Método    Caminho Ação
GET /produtos/ Lista todos
POST /produtos/ Cria novo produto
GET /produtos/1/ Mostra 1 produto
PUT /produtos/1/ Atualiza tudo
PATCH /produtos/1/ Atualiza parcial
DELETE /produtos/1/ Deleta produto

Já com Generics, nesse caso apenas lista e cria.
Rotas com Generics: GET /clientes/ → listar clientes | POST /clientes/ → criar cliente

urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProdutoViewSet, ClienteListCreateView

router = DefaultRouter()
router.register(r'produtos', ProdutoViewSet)

# No caso abaixo os nomes das rotas internas (para url name,  reverse) seriam: coisas, coisas/1 etc...
# router.register(r'produtos', ProdutoViewSet, basename="coisas")

urlpatterns = [
    path('', include(router.urls)),
    path('clientes/', ClienteListCreateView.as_view(), name='cliente-list-create'),
]

# OU... Sem o DefaultRouter, nesse caso usarei para update (parcial) e visualização de um único cliente
    path(
        'clientes/<int:pk>',
        views.ProdutoViewSet.as_view({
            'get': 'retrieve',
            'patch': 'partial_update',
        }),

Cria rotas automaticamente com base no nome da classe. pode usar o parametro basename para gerar com base nele. 
Caso não use o  DefaultRouter ou SimpleRouter, o Django precisa que você defina quais métodos http estará usando.
'get':'retrieve'#Recupera um único item com base no pk (ex: GET /clientes/1)
'delete':'destroy'#Remove um item com base no pk (ex: DELETE /clientes/1)
'get':'list',#Lista todos os itens (ex: GET /clientes/)
'post':'create',#Cria um novo item (ex: POST /clientes/)
'patch':'partial_update',#Atualiza parcialmente um item (ex: PATCH /clientes/1)
Caso Também precise sobrescrever (para personalizar) algum método da ViewSet use: retrieve, destroy e etc.

models.py
from django.db import models

class Categoria(models.Model):
    nome = models.CharField(max_length=100)

    def __str__(self):
        return self.nome

class Produto(models.Model):
    nome = models.CharField(max_length=100)
    preco = models.DecimalField(max_digits=10, decimal_places=2)
    categoria = models.ForeignKey(Categoria, on_delete=models.CASCADE)
    data_criacao = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.nome

class Cliente(models.Model):
    nome = models.CharField(max_length=100)
    email = models.EmailField(unique=True)

    def __str__(self):
        return self.nome

| BasicAuthentication

Usando o settings, faz com que todas as views do seu projeto exijam autenticação básica por padrão.

settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}


 

views_basic_auth.py
from rest_framework.authentication import BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response

class MinhaViewProtegida(APIView):
    authentication_classes = [BasicAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request):
        return Response({'mensagem': 'Você está autenticado!'})

produtos/
├── management/
│   └── commands/
│       └── script_criar_produtos.py

Crie essas pastas e arquivos dentro do seu app.

script_criar_produtos.py
from django.core.management.base import BaseCommand
from produtos.models import Produto, Categoria
from faker import Faker
import random

class Command(BaseCommand):
    help = 'Cria 10 produtos fictícios automaticamente'

    def handle(self, *args, **kwargs):
        fake = Faker()

        # Certifique-se de que exista pelo menos uma categoria
        categoria, created = Categoria.objects.get_or_create(nome="Categoria Padrão")

        for _ in range(10):
            nome = fake.word().capitalize()
            preco = round(random.uniform(10.0, 500.0), 2)

            produto = Produto.objects.create(
                nome=nome,
                preco=preco,
                categoria=categoria
            )

            self.stdout.write(self.style.SUCCESS(f'Produto criado: {produto.nome} - R${produto.preco}'))

Para gerar dados ficticios. instale a biblioteca Faker e rode python manage.py script_criar_produtos. Obs: Certifique-se de que tenha arquivos __init__.py em cada pasta (management/ e commands/), senão o Django não reconhece o comando.

| Paginação de API

settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 5  # número de itens por página
}

Para ativar a paginação em toda qualquer ViewSet ou APIView que retorne uma lista, no settings.py, adicione ou edite essa configuração

view_paginacao.py
from rest_framework.pagination import PageNumberPagination

class ProdutoPaginacaoCustomizada(PageNumberPagination):
    page_size = 3

class ProdutoViewSet(ModelViewSet):
    queryset = Produto.objects.all()
    serializer_class = ProdutoSerializer
    pagination_class = ProdutoPaginacaoCustomizada

Para aplicar em uma view especifica.

| Versionamento de API

Exemplo de organização recomendada:

seu_app/

├── views.py              # views da v1
├── views_v2.py           # views da v2
├── serializers.py        # serializers da v1
├── serializers_v2.py     # se necessário
├── urls.py               # urls da v1
├── urls_v2.py            # urls da v2
 

settings_versionamento.py
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
}
urls_principal.py
from django.urls import path, include

urlpatterns = [
    path('api/v1/', include('seu_app.urls')),
    path('api/v2/', include('seu_app.urls_v2')),
]

Irá conectar as versões

urls.py
from rest_framework.routers import DefaultRouter
from .views import ProdutoViewSet

router = DefaultRouter()
router.register(r'produtos', ProdutoViewSet)

urlpatterns = router.urls
urls_v2.py
from django.urls import path
from .views_v2 import ProdutoV2View  # Importa corretamente

urlpatterns = [
    path('produtos/', ProdutoV2View.as_view(), name='produtos-v2'),
]
views_v2.py
from rest_framework.views import APIView
from rest_framework.response import Response

class ProdutoV2View(APIView):
    def get(self, request):
        return Response({"mensagem": "Essa é a versão 2 da API de produtos"})

APIView te dá uma flexibilidade de sobrescresver os metodos get, post e etc.


Django Filter

O Django Filter é uma biblioteca que complementa o Django REST Framework, permitindo que você adicione filtros dinâmicos aos seus endpoints de API, de forma simples e limpa.

Ele permite que, ao acessar uma URL como: /api/produtos/?nome=banana&preco__lte=10.00, seja filtrado os resultados retornados pela API, sem precisar escrever essa lógica manualmente na view.

Instale o Biblioteca

bash
$ pip install django-filter

| Django Filters

settings_filter.py
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
INSTALLED_APPS = [
    # resto dos apps...
    'django_filters',
]
views_filter.py
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from .models import Produto
from .serializers import ProdutoSerializer

class ProdutoViewSet(ModelViewSet):
    queryset = Produto.objects.all()
    serializer_class = ProdutoSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['nome', 'preco', 'categoria']

filterset_fields define quais campos do modelo o usuário pode usar como filtros pela URL.

Agora é possivel fazer requisições como:
/api/produtos/?nome=Arroz
/api/produtos/?preco__lte=10
/api/produtos/?categoria=2

views_filters_2.py
from rest_framework.viewsets import ModelViewSet
from .models import Produto
from .serializers import ProdutoSerializer

#Importo filtros
from rest_framework import filters
from django_filters.rest_framework import DjangoFilterBackend

class ProdutoViewSet(ModelViewSet):
    queryset = Produto.objects.all()
    serializer_class = ProdutoSerializer

    # Aqui ativamos os filtros:
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter]

    # Filtro exato (/?nome=banana)
    filterset_fields = ['nome']

    # Busca com operador LIKE (/?search=ban)
    search_fields = ['nome', 'preco']

    # Ordenação (?ordering=preco ou ?ordering=-preco)
    ordering_fields = ['nome', 'preco']

Decorador actions

O decorador @action é usado com ViewSets do Django REST Framework (DRF) para adicionar ações personalizadas além das ações REST padrão (list, create, retrieve, update, destroy).
Serve para criar rotas extras no seu ViewSet, como:
  • /api/livros/{id}/marcar_como_lido/
  • /api/usuarios/{id}/ativar/
Essas ações não são parte do CRUD padrão, mas você pode adicioná-las de forma organizada no seu ViewSet.
views_com_actions.py
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import viewsets
from .models import Livro
from .serializers import LivroSerializer

class LivroViewSet(viewsets.ModelViewSet):
    queryset = Livro.objects.all()
    serializer_class = LivroSerializer

    @action(detail=True, methods=['post'])
    def marcar_como_lido(self, request, pk=None):
        livro = self.get_object()
        livro.lido = True
        livro.save()
        return Response({'status': 'Livro marcado como lido'})

Essa action cria a seguinte rota: POST /api/livros/1/marcar_como_lido/. Sem o @action, você teria que criar outra APIView ou Router manualmente.
detail=True: Indica que a ação é para um item específico (exige o pk na URL).
detail=False: A ação será para a coleção inteira (sem pk na URL).
methods=['post']: Define os métodos HTTP permitidos para essa ação


Test no Django Rest Framework (DRF)

No DRF usa-se a classe from rest_framework import test, para efetuar testes. Geralmente ao fazer testes no rest api do django, não é necessário programas externos como Postman.


JWT (JSON Web Token)

JWT (JSON Web Token) é um padrão aberto (RFC 7519) usado para transmitir informações seguras entre duas partes como um token compactado em formato JSON, normalmente usado para autenticação e autorização em APIs. ler mais.
  • Login: O usuário envia suas credenciais (ex: email e senha).
  • Token gerado: Se estiver tudo certo, o servidor cria um JWT com os dados do usuário (ex: id, nome) e envia para o cliente.
  • Uso do token: O cliente envia esse token em cada requisição no header Authorization (Authorization: Bearer <TOKEN>)
  • Validação: O servidor decodifica o token, verifica a assinatura, e autoriza a ação se for válido.

Em DRF deve-se instalar a biblioteca externa: djangorestframework-simplejwt. leia mais.

🔐 Etapas do login DRF com JWT (via lib djangorestframework-simplejwt):
  1. Você envia os dados de login (email/usuário + senha) para a URL: POST /api/token/ { "username": "seu_usuario", "password": "123456" }
  2. O SimpleJWT usa um serializer chamado TokenObtainPairSerializer, que:
    • Busca o usuário no banco de dados (via authenticate()), verifica os usuários da tabela auth_user, padrão do django.
    • Verifica se a senha está correta (comparando com o hash).
    • Se estiver tudo ok, gera os tokens (access e refresh).
settings_autenticacao_jwt.py
import os
from datetime import timedelta

INSTALLED_APPS = [
    'rest_framework_simplejwt',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
}

# Personalizar o token, opcional.
SIMPLE_JWT = {
    # Tempo de vida do token de acesso (Access Token). Recomendado 5 minutos.
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
    # obtém um novo Access Token sem precisar fazer login novamente.
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    # Define se o refresh antigo será automaticamente invalidado após uso (entrando na blacklist)
    'BLACKLIST_AFTER_ROTATION': False,
    # Chave secreta usada para assinar e verificar os JWTs. Nunca use INSECURE em produção
    'SIGNING_KEY': os.environ.get('SECRET_KEY_JWT', 'INSECURE'),
    # Define o prefixo usado no cabeçalho Authorization.
    'AUTH_HEADER_TYPES': ('Bearer',),
}

Adicione essa configuração.

urls_jwt.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns += [
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),  # Login
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),  # Refresh token
]

Adicione as rotas, e depois faça uma requisição para autenticação para o endpoint da rota (nesse exemplo: POST /api/token/):
{"username": "seu_usuario","password": "sua_senha"}. Receberá uma resposta tal como essa:{"access": "token.jwt.aqui","refresh": "refresh.token.jwt"}. Para usar o token nas proximas requisições basta enviar no Header: Authorization: Bearer SEU_TOKEN_AQUI. quando o token expirar use: POST /api/token/refresh/

view_jwt.py
# exemplo simples de uso
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response

class MinhaViewProtegida(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        return Response({"mensagem": "Você está autenticado com JWT!"})


# Exemplo com personalização de acesso usando o permission.py:
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status

from .models import MinhaModel
from .serializers import MinhaModelSerializer
from .permissions import IsOwner  # certifique-se de que está no arquivo permissions.py

class MinhaViewSet(viewsets.ModelViewSet):
    # Define o queryset e o serializer usados pela view
    queryset = MinhaModel.objects.all()
    serializer_class = MinhaModelSerializer
    permission_classes = [IsAuthenticated]  # Permissão padrão
   http_method_names = ['get', 'options', 'head', 'patch', 'post', 'delete'] # posso definir os métodos permitidos

    # Define permissões personalizadas dependendo do método HTTP
    def get_permissions(self):
        # Se o método for PATCH ou DELETE, exige que seja o dono
        if self.request.method in ['PATCH', 'DELETE']:
            return [IsOwner()]
        # Caso contrário, usa as permissões padrão
        return super().get_permissions()

    # Lista os objetos
    def list(self, request, *args, **kwargs):
        # Exibe o usuário da requisição no terminal (apenas debug)
        print('REQUEST', request.user)
        # Exibe se o usuário está autenticado
        print(request.user.is_authenticated)
        # Executa o método padrão de listagem
        return super().list(request, *args, **kwargs)

    # Sobrescreve o método retrieve apenas como exemplo de uso de check_object_permissions
    def retrieve(self, request, *args, **kwargs):
        # Obtém o objeto
        obj = self.get_object()
        # Verifica se o usuário tem permissão para acessar o objeto
        self.check_object_permissions(self.request, obj)
        # Retorna o objeto serializado
        serializer = self.get_serializer(obj)
        return Response(serializer.data)

    # Também como exemplo: sobrescrevendo destroy com verificação de permissão
    def destroy(self, request, *args, **kwargs):
        # Obtém o objeto
        obj = self.get_object()
        # Verifica permissão de acesso ao objeto
        self.check_object_permissions(self.request, obj)
        # Deleta o objeto
        obj.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Exemplo de view com JWT.  check_object_permissions é um método da classe APIView (que o ModelViewSet herda). Serve para aplicar permissões a nível de objeto (e não só da requisição). Ele chama todos os has_object_permission(...) das classes de permissão listadas em permission_classes. É usado dentro de get_object() ou métodos que acessam diretamente um objeto.

permissions.py
from rest_framework import permissions

class IsOwner(permissions.BasePermission):
    """
    Permissão que só permite acesso a objetos do próprio usuário.
    """

    def has_object_permission(self, request, view, obj):
        # Garante que o objeto pertence ao usuário autenticado
        return obj.user == request.user

    def has_permission(self, request, view):
        # Só permite acesso se o usuário estiver autenticado
        return request.user and request.user.is_authenticated

Arquivo que criei dentro do app, para checar dois tipos de permissão.
BasePermission é uma classe base (ou seja, "modelo") do Django REST Framework usada para criar regras de acesso personalizadas às suas views ou objetos. Quando você herda de BasePermission, você pode criar suas próprias permissões, definindo dois métodos principais: has_permissionhas_object_permission.

Exemplo_com_metodos_seguros.py
# no arquivo permission.py
from rest_framework import permissions

class IsOwner(permissions.BasePermission):
    """
    Permite acesso total apenas ao dono do objeto.
    Usuários não autenticados só podem ler (GET).
    """

    def has_object_permission(self, request, view, obj):
        # Permite leitura para qualquer método seguro (GET, HEAD, OPTIONS)
        if request.method in permissions.SAFE_METHODS:
            return True

        # Edição ou exclusão somente se o objeto for do usuário autenticado
        return obj.user == request.user

    def has_permission(self, request, view):
        # Permite acesso de leitura a todos, mas ações de escrita apenas a usuários autenticados
        return (
            request.method in permissions.SAFE_METHODS
            or request.user and request.user.is_authenticated
        )


# no arquivo views.py
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.views import APIView
from rest_framework.response import Response

from .permission import IsOwner

class MinhaViewProtegida(APIView):
    # Aplica múltiplas permissões: deve ser autenticado para escrever, e ser dono do objeto
    permission_classes = [IsAuthenticatedOrReadOnly, IsOwner]

    def get(self, request):
        return Response({"mensagem": "Você pode visualizar essa mensagem mesmo sem login."})

    def post(self, request):
        return Response({"mensagem": f"{request.user}, você postou com sucesso!"})

Exemplo de uso para leitura para todos e escrita para o dono.