Using Django REST framework, how to avoid fetching an object in both serializer and view?

Let's say one of my POSTed DRF API params is a string that allows me to identify an object. My serializer then uses this field to validate the string (together with some other POSTed params) and fetch the underlying object (as the final part of the validation). So, at the end of my validate(), I have already looked up the object I need to work with in my view, but I can't figure out how to access this object in the view. If I set data['my_object'] = my_object at the end of validate(), it ends up being just the name of my object after serializer.is_valid() returns in my view.

How do I "pass" objects from the serializer back to the view? (Or, if there's a reason not to do that, what's the best practice to avoid looking up the same object in the database twice?)

# serializer
class MySerializer(serializers.Serializer):
    my_string_param = serializers.CharField()
    another_param = serializers.CharField()
    ...
    def validate(self, data):
        # validate data['my_string_param'] and fetch my_object
        try:
            my_object = MyModel.objects.get(
                field1=data['my_string_param'], field2=data['another_param'])
        except MyModel.DoesNotExist:
            raise ValidationError('Please check your params and try again.')
        data['my_object'] = my_object  # doesn't work: is a string when later accessed in the view 
        return data

# view
class MyView(APIView):
    ...
    def post(self, request, format=None):
        serializer = MySerializer(data=request.data)
        if serializer.is_valid():
            # don't want to fetch from db again
            # my_object = MyModel.objects.get(field1=serializer.data['my_string_param'], field2=serializer.data['another_param'])

            # would like to reuse already fetched object, but it's just its str name
            my_object = serializer.data['my_object']

            # do stuff with my_object and return
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Answers


You can set the fetched object as a serializer attribute and then access this attribute in your view. Doing this will avoid an extra query in your view.

# serializer
class MySerializer(serializers.Serializer):
    my_string_param = serializers.CharField()
    another_param = serializers.CharField()
    ...
    def validate(self, data):
        # validate data['my_string_param'] and fetch my_object
        try:
            # set the object as an attribute
            self.my_object = MyModel.objects.get( 
                field1=data['my_string_param'], field2=data['another_param'])
        except MyModel.DoesNotExist:
            raise ValidationError('Please check your params and try again.')
        return data

# view
class MyView(APIView):
    ...
    def post(self, request, format=None):
        serializer = MySerializer(data=request.data)
        if serializer.is_valid():            
            my_object = serializer.my_object # access the already retrieved object 

            # do stuff with my_object and return
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Need Your Help

Is it possible to change the cache_classes config setting in Rails dynamically?

ruby-on-rails ruby ruby-on-rails-3

I have a little interactive script for doing a bunch of things with my Rails app. Generally I run this with the cache_classes config option disabled so that I can modify and reload views without h...