Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DRF APIView move request validation to dispatch method using request.data

I've created a base api view, which extends from APIView, where I log response time, log request, and other common stuffs.

Now, I also want to add request validation here, using the Serializer defined in sub-class Views. I thought the appropriate place is to put that in dispatch() method. But before I call API.dispatch() method, request.data is not prepared. So, that won't work. Can someone help me in right direction as to how to move validation to a single place?

Here's the class structure:

class BaseView(APIView):
    validation_serializer = None

    def dispatch(self, request, *args, **kwargs):
        # Some code here
        # How to use `validation_serializer` here, to validate request data?
        # `request.data` is not available here.
        response = super(BaseView, self).dispatch(request, *args, **kwargs)
        # Some code here
        return response

class MyView(BaseView):
    validation_serializer = ViewValidationSerializer

    def post(self, request, *args, **kwargs):
        pass

I thought another approach could be use decorator on the top of post() method. But if only there was an cleaner way, than putting decorators all across the project?

Note: It's similar to the question here: Django - DRF - dispatch method flow. But as per the suggestion there, I don't want to just copy the entire dispatch method from DRF source code.

like image 924
Rohit Jain Avatar asked Nov 20 '16 07:11

Rohit Jain


1 Answers

The method that processes the django request into a DRF request (and adds the request.data property) is the APIView.initialize_request . The APIView.dispatch() method calls it and then proceeds to call the appropriate method handler (post/patch/put).

You can try to do that yourself by calling it and using the returned object:

class BaseView(APIView):
    validation_serializer = None

    def dispatch(self, request, *args, **kwargs):
        request = self.initialize_request(request, *args, **kwargs)
        kwargs['context'] = self.get_serializer_context()
        serializer = self.validation_serializer(data=request.data, *args, **kwargs)

        # use `raise_exception=True` to raise a ValidationError
        serializer.is_valid(raise_exception=True)

        response = super(BaseView, self).dispatch(request, *args, **kwargs)
        return response

However, I would suggest against this, as other functionality of dispatch() probably should be performed prior to handling validation; thus, you could move the above logic to the relevant post/patch/put methods instead.

In these methods you can also use self.request directly since it was already initialized by dispatch().

like image 176
tutuDajuju Avatar answered Oct 26 '22 12:10

tutuDajuju