Add a field to serializer context

Viewed 589

I have a retrieveAPIView and I have to set one of its field to the serializer_context

class FoodRetrieveAPIView(RetrieveAPIView):

    serializer_class = serializers.FoodRetrieveSerializer
    permission_classes = [IsAuthenticated]
    queryset = models.Food.objects.all()

    def get_serializer_context(self):
        context = super().get_serializer_context()
        query_params = self.request.query_params
        delivery_date = query_params.get("delivery_date")
        context["delivery_date"] = delivery_date

And in my serializer, I have to access the context

class FoodRetrieveSerializer(serializers.ModelSerializer):
    items = FoodItemSerializer(many=True)


class FoodItemSerializer(serializers.ModelSerializer):
    item = serializers.SerializerMethodField()

    def get_item(self, obj):
    return ItemValueRetrieveSerializer(
        instance=obj.get_item(), many=False, context=self.context
    ).data



class ItemValueRetrieveSerializer(serializer.ModelSerializer):
    value = serializers.SerializerMethodField()

    def get_value(self, obj):
        delivery_date = self.context.get("delivery_date")
        food_value = obj.get_food_value(delivery_date=delivery_date)

I've been trying this way a couple of times but i can't still get the value of delivery_date in context. How can i do this? Thanks!

1 Answers

First, make sure you're sending a GET request to an URL such as http://localhost:8000/.../some_pk?delivery_date=2021-01-31 since Django expects this kind of date format by default.

Second point, don't forget to return the context at the end of get_serializer_context:

class FoodRetrieveAPIView(RetrieveAPIView):
    # ...
    def get_serializer_context(self):
        context = super().get_serializer_context()
        query_params = self.request.query_params
        delivery_date = query_params.get("delivery_date")
        context["delivery_date"] = delivery_date
        return context

Third and last point, as you have nested serializer, you need to pass the context each time you call a child serializer as a domino effect:

class FoodRetrieveSerializer(serializers.ModelSerializer):
    # don't forget to pass the context here
    items = FoodItemSerializer(many=True, context=self.context)


class FoodItemSerializer(serializers.ModelSerializer):
    item = serializers.SerializerMethodField()

    def get_item(self, obj):
        # This function looks weird to me, but I assume you know what you're doing
        return ItemValueRetrieveSerializer(
            instance=obj.get_item(), many=False, context=self.context
        ).data


class ItemValueRetrieveSerializer(serializer.ModelSerializer):
    value = serializers.SerializerMethodField()

    def get_value(self, obj):
        delivery_date = self.context.get("delivery_date")
        food_value = obj.get_food_value(delivery_date=delivery_date)

Also, just a piece of advice, the dictionary get() method accepts a second argument which is the default value in case the key does not exist. It's always better to explicitly give it even if it's None. And in this case, probably consider handling the None case by raising a HTTP 400 bad request error with a custom error message saying you need to give a delivery date (rather than a 500 server error).

Related