Django REST Framework like a ninja

Hello everyone. This post is to show you all the tricks that I’ve been learning until now about Django REST Framework and create the best api for your app :D

Check this page is awesome, you can navigate through the DRF classes and check all the code :D Thanks to @_aericson at @vintasoftware

ViewSets(Model)

You can overwrite any function that I will mention, in the ViewSet class.

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset for viewing and editing user instances.
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()

-The simpliest model viewset

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset for viewing and editing user instances.
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()

-Use a serializers according to the request

def get_serializer_class(self):
    if self.action == 'list':
        return CustomListSerializer
    if self.action == 'create':
        return CustomistCreateSerializer
    if self.action == 'retrieve':
        return CustomRetrieveSerializer
    if self.action == 'update':
        return CustomUpdateSerializer
    if self.action == 'partial_update':
        return CustomPartialUpdateSerializer
    if self.action == 'destroy':
        return CustomDestroySerializer
    return CustomNormalSerializer

-Custom each request

The belows functions are the real one of DRF, you can edit them and overwrite as your needs

def list(self, request, *args, **kwargs):
    queryset = self.filter_queryset(self.get_queryset())

    page = self.paginate_queryset(queryset)
    if page is not None:
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)

    serializer = self.get_serializer(queryset, many=True)
    return Response(serializer.data)


def create(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)
    headers = self.get_success_headers(serializer.data)
    return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)


def retrieve(self, request, *args, **kwargs):
    instance = self.get_object()
    serializer = self.get_serializer(instance)
    return Response(serializer.data)


def update(self, request, *args, **kwargs):
    partial = kwargs.pop('partial', False)
    instance = self.get_object()
    serializer = self.get_serializer(instance, data=request.data, partial=partial)
    serializer.is_valid(raise_exception=True)
    self.perform_update(serializer)

    if getattr(instance, '_prefetched_objects_cache', None):
        # If 'prefetch_related' has been applied to a queryset, we need to
        # refresh the instance from the database.
        instance = self.get_object()
        serializer = self.get_serializer(instance)

    return Response(serializer.data)


def partial_update(self, request, *args, **kwargs):
    kwargs['partial'] = True
    return self.update(request, *args, **kwargs)


def destroy(self, request, *args, **kwargs):
    instance = self.get_object()
    self.perform_destroy(instance)
    return Response(status=status.HTTP_204_NO_CONTENT)

-Create a post list like this:

[
    {
        "id": 26,
        "workShiftStart": "2017-02-02",
        "workShiftEnd": "2018-02-02",
        "personStart": "2018-02-02",
        "workShift": 74,
        "person": {
            "id": 206,
            "nombre": "NICOLAS"
        }
    },
    {
        "id": 27,
        "workShiftStart": "2018-02-02",
        "workShiftEnd": "2018-02-02",
        "personStart": "2018-03-01",
        "workShift": 74,
        "person": {
            "id": 112,
            "nombre": "Pablo"
        }
    },
]

Where each JSON Object is a model in our Django models. So to create a list where you create all the elements, or none elements (atomic creation) we have to overwrite the create function of our ViewSet Class

def create(self, request):
    serializer = WorkShiftPersonCreateSerializer(data=request.data, many=True)
    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)

Here, the key is the many=True that create a serialize that contain many objects and then you can process them like just one object but they are multiples objects.

Serializers

-Create an array of objects in a object like this:

{
    "name": "Turno raro",
    "holidaysConsider": false,
    "tolerance": 5,
    "shiftType": "R",
    "days": [
        {
            "hasSnack": false,
            "snackTime": 2.0,
            "workShiftDay": "1",
            "start": "00:00:00",
            "end": "23:59:00",
            "workDay": true
        },
        {
            "hasSnack": false,
            "snackTime": 2.0,
            "workShiftDay": "1",
            "start": "00:00:00",
            "end": "23:59:00",
            "workDay": true
        }
    ]
}

where this JSON is a workshift that contain days, and you can to save the workshift but also each day with DRF as well and make the relation with the workshift that is a FK(Foreign Key) relation. The way to do that is overwritting the create method in the workshift serializer:

class WorkShiftSerializer(serializers.ModelSerializer):

    days = DayWorkShiftSerializer(many=True, read_only=False)

    class Meta:
        model = WorkShift
        fields = ('id','name','holidaysConsider','tolerance','shiftType', 'days')
        read_only_fields = ('id',)

    def create(self, validated_data):
        days = validated_data.pop('days')
        workShift = WorkShift.objects.create(**validated_data)
        for day in days:
            Day.objects.create(workShift=workShift, **day)
        return workShift

Here in the create function, we’ll recieve the data(JSON) and we will pop the days. Then create the workshift that will have this data:

{
    "name": "Turno raro",
    "holidaysConsider": false,
    "tolerance": 5,
    "shiftType": "R",
}

and then for each day, create it and make the relations :D

Routings