Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change viewset retrieve response in Django Rest Framework?

I'm currently developing a Web App that uses an API as the backend for a University Project.

And I've read that DRF is the fastest and easiest way to develop and deploy an API, I already followed through their entire official

documentation and I don't seem to understand how I could the following in their ViewSet and Serializer.

Here's one endpoint of my API called airports.

All airports available in the USA

Returns json/csv list of links to the available airports in the USA.

  • URL

    /airports

  • Method:

    GET

  • Success Response:

    • Code: 200
      Content:

      [
        {
            "airport": {
                "code": "PHL",
                "name": "Philadelphia, PA: Philadelphia International",
              "id": 123,
                "url": "/airports/123"
            },
        {
            "airport": {
                "code": "AHR",
                "name": "American Hour Rapid",
            "id": 125,
                "url": "/airports/125"
        }
        .
        .
        .
      ]
    

Show Airport Informations

Returns all links to the carriers operating at a specific airport, a link to the related statistics on a specific month and year and also a link to the airport routes. If in case neither the year or the month are specified, the default will be the one with the most recent date.

  • URL

/airports/:id

  • Method:

GET

  • URL Params

    Required:

    id=[integer]

  • Success Response:

{
    "airport": {
      "code": "PHL",
      "name": "Philadelphia, PA: Philadelphia International",
      "id": 123,
      "url": "/airports/123"
    },
    "routes_link": "/airports/123/routes",
    "carriers": [
        {
          "id": 124,
          "url": "/carriers/124?airport_id=123",
          "statistics_url":"/airports/1carrier=124&statistics='flights'"
        },
        .
        .
        .
      ]
}

I was able to do /airports properly listing all of the available airports in the database but when using the ViewSet I don't know how to "customize" the response when trying to retrieve information about only one airport specified by the id and in the application the routes are going to be generated dynamically and I was planning to add to the response body and not another field in the model.

Models:

class Carrier(models.Model):
    code = models.CharField(max_length=10)
    name = models.TextField()
    #airports = models.ManyToManyField(Airport) 

    def __str__(self):
        return self.name

class Airport(models.Model):
    code = models.CharField(max_length=10)
    name = models.TextField()
    carriers = models.ManyToManyField(Carrier, related_name='airports')

    def __str__(self):
        return self.name

Serializers:

class AirportSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Airport
        fields = ('id', 'name', 'code', 'url')

class CarrierSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Carrier
        fields = ('id', 'name', 'code', 'url')

View:

class AirportList(viewsets.ModelViewSet):
    queryset = models.Airport.objects.all()
    serializer_class = AirportSerializer
    # @Override something here?

Anyone has a tip on how I could approach this using DRF or any kind of learning material I could use?

like image 601
murthaA Avatar asked Jan 01 '23 04:01

murthaA


2 Answers

If you want to modified retrieve functionality of ModelViewset you can overwrite its retrieve method and do what ever you want. mixin's link

class AirportList(viewsets.ModelViewSet):
    queryset = models.Airport.objects.all()
    serializer_class = AirportSerializer
    def retrieve(self, request, *args, **kwargs):
        # do your customization here
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

How to explore DRF

I think best way to look after any new thing is their codebase. For ModelViewset you should start from viewslink and explore what are the functionality it provide and how i can customize them.

like image 99
Shakil Avatar answered Jan 14 '23 02:01

Shakil


as @Shakil mentioned above you need to overwrite retrieve method, but to make a custom response you need to include self.get_object() in a Try Except block, as without it if get_object fails it would fallback to the default response and won't have the chance to custom it.
So you can use something like that...

First the custom response class (for failing cases for example)

class ErrorResponse(Response):
    def __init__(self, *args, **kwargs):
        super(ErrorResponse,self).__init__(*args, **kwargs)
        self.status_code = 404
        self.data = {
            'success': False,
            'message': args[0].get('message')
        }

and the retrieve method

def retrieve(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
        except Exception as e:
            return ErrorResponse({'message':str(e)})
        else:
            #any additional logic
            serializer = self.get_serializer(instance)
            return Response({'data': serializer.data})
like image 45
Mostafa Garana Avatar answered Jan 14 '23 02:01

Mostafa Garana