Aniket Maithani
Aniket Maithani

Reputation: 865

Add extra field in response output in DRF 3.0

I have the following models

class Restaurant(models.Model):
    name_of_the_restaurant = models.CharField(max_length=30, blank=True)
    opening_time = models.TimeField(auto_now=False, auto_now_add=False)
    closing_time = models.TimeField(auto_now=False, auto_now_add=False)

And

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    city = models.CharField(max_length=30, blank=True)
    country = models.CharField(max_length=30, blank=True)
    postal_code = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)
    favourite_restaurant = models.ManyToManyField(Restaurant,
                                                  blank=True,
                                                  related_name='favourite_restaurant',
                                                  related_query_name='favourite_restaurant')

I have defined a serializer for Restaurant model which is mainly :

class RestaurantSerializer(serializers.ModelSerializer):

    class Meta:
        model = Restaurant
        fields = '__all__'

Now in my ViewSet logic I am doing the following :

class RestaurantListView(generics.ListAPIView):
    serializer_class = RestaurantSerializer

    def get_queryset(self):
        queryset = {'Error': 'Please pass valid url parameters'}
        city = self.request.query_params.get('city', None)
        postal_code = self.request.query_params.get('postalcode', None)
        country = self.request.query_params.get('country', None)
        if city is not None or postal_code is not None:
            queryset = Restaurant.objects.filter(
                Q(city=city) | Q(pincode=postal_code))
        if country and city is not None and postal_code is None:
            queryset = Restaurant.objects.filter(country=country, city=city)
        return queryset

    def get(self, request, format=None):
        restaurant_qs = self.get_queryset()
        ids_list = [restaurant.id for restaurant in restaurant_qs]
        favourite_restaurant = is_favourite_restaurant(ids_list, self.request.user)
        serializer = RestaurantSerializer(restaurant_qs, many=True)
        return Response(serializer.data)

where is_favourite_restaurant is a custom function function which returns queryset of FAVOURITE restaurant(s) of a user. Now in the output for this GET request I am getting result as :

[
    {
        "id": 2,
        "name_of_the_restaurant": "Aniket",
        "opening_time": "14:08:33.413402",
        "closing_time": "22:08:33.413414"
    },
    {
        "id": 3,
        "name_of_the_restaurant": "Aniket-1",
        "opening_time": "14:13:37.656385",
        "closing_time": "22:13:37.656397"
    }
]

Whereas the desired output I want is to append an extra field is_favourite:true to that restaurant which user has previously marked favourite. And hence the output should be

[
    {
        "id": 2,
        "name_of_the_restaurant": "Aniket",
        "opening_time": "14:08:33.413402",
        "closing_time": "22:08:33.413414",
        "is_favourite": true, 
    },
    {
        "id": 3,
        "name_of_the_restaurant": "Aniket-1",
        "opening_time": "14:13:37.656385",
        "closing_time": "22:13:37.656397"
    }
]

EDIT :

Definition of is_favourite_restaurant function :

def is_favourite_restaurant(restaurant_qs, user):
    favourite_restaurant_qs = Profile.objects.get(user=user).favourite_restaurant.filter(
        pk__in=restaurant_qs.values_list('id', flat=True))
    return favourite_restaurant_qs

Upvotes: 0

Views: 1320

Answers (1)

Utkucan Bıyıklı
Utkucan Bıyıklı

Reputation: 1117

You can use SerializerMethodField. SerializerMethodField allows add extra field which is read only as you want.

class RestaurantSerializer(serializers.ModelSerializer):
    is_favorite = serializers.SerializerMethodField()

    class Meta:
        model = Restaurant
        fields = ('your', 'fields', 'is_favorite')

    def get_is_like(self, obj):
        return is_favourite_restaurant(obj.id, self.context['request'].user)

Normally, ListAPIView add context to serializer. As you use your create method, you should add manually.

serializer = RestaurantSerializer(restaurant_qs, many=True, context={'request': self.request})

Context allows access some data which is we send from the view.
As you did not shown your is_favourite_restaurant, i can't say that what should you do in that function. I guess you should change ids parameter from array to one id.

Your response looks like

[
    {
        "id": 2,
        "name_of_the_restaurant": "Aniket",
        "opening_time": "14:08:33.413402",
        "closing_time": "22:08:33.413414",
        "is_favourite": True, 
    },
    {
        "id": 3,
        "name_of_the_restaurant": "Aniket-1",
        "opening_time": "14:13:37.656385",
        "closing_time": "22:13:37.656397",
        "is_favourite": False, 

    }
]

def is_favourite_restaurant(restaurant_id, user):
    favourite_restaurant_qs = Profile.objects.get(user=user).favourite_restaurant.filter(
        pk=restaurant_id).exists()
    return favourite_restaurant_qs

Upvotes: 1

Related Questions