Django REST Framework - HelpingHandPT/COVID19-be GitHub Wiki

Introdução

Os exemplos providenciados nesta página de Wiki terão como por base o seguinte simples modelo:

class Student(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=20)
    score = models.DecimalField(max_digits=10,decimal_places=3)

    def __str__(self):
        return self.id+self.name+self.score

CBVs e FBVs

Através do Django REST Framework podemos criar RESTful APIs através de Class Based Views ou Function Based Views.

Através de CBVs conseguimos criar com três linhas de código todas as operações CRUD de um modelo ou de uma tabela da base de dados. Se quisermos escrever lógica de negócios de uma forma mais customizável, para isso usamos FBVs.

Nota: além da documentação extensiva no site do DRF, existe ainda Classy Django REST Framework que providencia um interface navegável com métodos e atributos para todas as CBVs do DRF.

Serializers

Uma outra funcionalidade oferecida por DRF são Serializers. Estes são simples de criar e úteis quando um pedido REST chega no protocolo HTTP (XML, JSON, CSV, ...), convertendo o conteúdo desses formatos em objetos Python que poderão ser, posteriormente, armazenados na BD (o oposto também se verifica, ou seja, também podem converter objetos Python vindos da BD em JSON, XML, ...).

Exemplo:

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model= Student
        fields=['id','name','score']

Se os estudantes pudessem fazer pedidos, teríamos de usar Nested Serializers

Exemplo:

class PedidosSerializer(serializers.ModelSerializer):
    class Meta:
        model=Pedidos
        fields='__all__' #nomenclatura para introduzir todos os campos do modelo

class StudentSerializer(serializers.ModelSerializer):
    pedidos = PedidosSerializer(read_only=True,many=True) # many=True significa que um Student pode ter mais do que um pedido
    class Meta:
        model = Student
        fields='__all__'

O modelo Pedidos seria algo semelhante a isto

class Pedidos(models.Model):
    title = models.CharField(max_length=20)
    description=models.CharField(max_length=10)
    student= models.ForeignKey(Student,related_name='students',on_delete=models.CASCADE)

ORM

Outra funcionalidade útil é o Django Object Relational Mapping (ORM) que permite criar através de métodos operações de BD sem escrever qualquer linha de SQL.

Exemplo:

Student.objects.all()
Student.objects.get(pk=pk)

Web Browsable API

Para navegar pelo REST API, o DRF providencia um Browsable API onde poderemos realizar todas as operações HTTP (GET, POST, PUT, ...). É útil para operações CRUD, não substituíndo uma ferramenta como o Postman para certos pedidos.

DRF Browsable API GUI

Paginação

DRF inclui suporte para estilos de Paginação personalizáveis. Isso permite modificar o quão grandes os conjuntos de resultados são divididos em páginas de dados individuais.

Segurança

DRF suporta criação de Autenticação, Autorização e OAuth. Para isso basta apenas configurarmos, ou seja, não precisaremos de muito código para tal.

Componentes

Request

A classe Request estende o HttpRequest, adicionando suporte ao processamento de pedidos e autenticação.

Response

A estrutura REST suporta a negociação de conteúdo HTTP, fornecendo uma classe Response, que permite retornar conteúdo que pode ser renderizado de várias formas, consoante o pedido do cliente.

A classe Response subclassa SimpleTemplateResponse do Django. Os objetos Response são inicializados com dados, que devem consistir em primitivas nativas do Python. A estrutura REST usa a negociação de conteúdo HTTP padrão para determinar como deve renderizar o conteúdo da resposta final.

Status Codes

O conjunto completo de HTTP Status Codes incluídos no módulo de status está listado na documentação. Este mesmo módulo também inclui um conjunto de funções auxiliares para testar se um Status Code está é de um determinado tipo.

Exemplo:

status.HTTP_201_CREATED
status.HTTP_400_BAD_REQUEST

@api_view e APIView

O decorador @api_view exibe uma lista de métodos HTTP aos quais view deve responder (este é usado nas FBVs).

Exemplo:

@api_view(['GET','POST'])
def student_list(request):

    if request.method =='GET':
        students = Student.objects.all()
        serializer=StudentSerializer(students,many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = StudentSerializer(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)

@api_view(['GET','PUT','DELETE'])
def student_detail(request,pk):
    try:
        student = Student.objects.get(pk=pk)
    except Student.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method=='GET':
        serializer = StudentSerializer(student)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = StudentSerializer(student,data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        student.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

APIView, por sua vez, é uma classe que todas as CBVs devem extender.

Exemplo:

class StudentList(APIView):

    def get(self,request):
        students = Student.objects.all()
        serializer = StudentSerializer(students,many=True)
        return Response(serializer.data)

    def post(self,request):
        serializer = StudentSerializer(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 StudentDetail(APIView):
    def get_object(self,pk):
        try:
            return Student.objects.get(pk=pk)
        except Student.DoesNotExist:
            raise Http404

    def get(self,request,pk):
        student = self.get_object(pk)
        serializer = StudentSerializer(student)
        return Response(serializer.data)

    def put(sefl,request,pk):
        student=self.get_object(pk)
        serializer=StudentSerializer(student,data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)

    def delete(self,request,pk):
        student = self.get_object(pk)
        student.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

CBVs

Mixins

As classes mixin fornecem as acções que são usadas para fornecer o comportamento básico das vistas. Observemos que as classes mixin fornecem métodos de acção em vez de definir os métodos handler, como .get() e .post(), diretamente. Isso permite uma composição de comportamento mais flexível.

métodos Action classes Mixin métodos Handler
list() ListModelMixin get()
create() CreateModelMixin post()
retrieve() RetrieveModelMixin get()
update() UpdateModelMixin put()
delete() DestroyModelMixin delete()

Exemplo:

class StudentList(mixins.ListModelMixin,mixins.CreateModelMixin,generics.GenericAPIView):
    queryset = Student.objects.all()
    serializer_class= StudentSerializer

    def get(self,request):
        return self.list(request)

    def post(self,request):
        return self.create(request)

class StudentDetail(mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,generics.GenericAPIView):
    queryset = Student.objects.all()
    serializer_class= StudentSerializer

    def get(self,request,pk):
        return self.retrieve(request,pk)

    def put(self,request,pk):
        return self.update(request,pk)

    def delete(self,request,pk):
        return self.destroy(request,pk)

Generics

Generics fornecidas pela estrutura REST permitem criar rapidamente views da API. Se as generics não atenderem às necessidades da API, podemos usar a classe APIView ou reutilizar os mixins e as classes base usadas pelas generics.

Generics
CreateAPIView
ListAPIView
RetrieveAPIView
DestroyAPIView
UpdateAPIView
ListCreateAPIView (operações sem id - coleções de instâncias do modelo)
RetrieveUpdateAPIView
RetrieveDestroyAPIView
RetrieveUpdateDestroyAPIView

Exemplo:

class StudentList(generics.ListCreateAPIView):
    queryset = Student.objects.all()
    serializer_class= StudentSerializer`

class StudentDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Student.objects.all()
    serializer_class= StudentSerializer

ViewSets

Uma classe ViewSet é simplesmente um tipo de CBV, que não fornece manipuladores de métodos, como .get () ou .post (), e fornece ações como .list () e .create (). Extendendo uma classa a partir de ViewSet precisaremos de providenciar a implementação para list(), create(), retrieve(), update() e delete(). Pos sua vez, com ModelViewSet não precisamos de escrever quase código. Exemplo:

class StudentViewSet(viewsets.ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer

Com ViewSets não precisamos de configurar os URLs por nós mesmos. Podemos utilizar Routers. Exemplo:

router = DefaultRouter()
router.register('students',views.StudentViewSet)

urlpatterns = [
    path('', include(router.urls)),
]