FormParser and MultiPartParser are never invoked · Issue #1346 · encode/django-rest-framework (original) (raw)

While writing my own parser subclass to override handling of application/x-www-form-urlencoded, I discovered a startling bug: FormParser and MultiPartParser are never invoked, nor is it possible to use my own parser when the media type is application/x-www-form-urlencoded or multipart/form-data.

I tracked down the code responsible for this, which is in Request._perform_form_overloading:

# At this point we're committed to parsing the request as form data.
self._data = self._request.POST
self._files = self._request.FILES

Then _load_data_and_files sees _data is there, and doesn't call _parse.

Here are some test cases which illustrate the issue. The first two fail, and the third passes.

from rest_framework import parsers

class TestFormParser(TestCase):

def setUp(self):
    class TestView(APIView):
        class TestParser(parsers.FormParser):
            def parse(self, stream, media_type=None, parser_context=None):
                return {'replacing_all_the_content': 'yes'}
        parser_classes = (TestParser,)
        def post(self, request, *args, **kwargs):
            return Response(request.DATA)
    self.view = TestView.as_view()

def test_form_parser_called(self):
    request = factory.post('/', 'foo=bar', content_type='application/x-www-form-urlencoded')
    response = self.view(request)
    self.assertEquals(response.data, {'replacing_all_the_content': 'yes'})

class TestMultipartParser(TestCase):

def setUp(self):
    class TestView(APIView):
        class TestParser(parsers.MultiPartParser):
            def parse(self, stream, media_type=None, parser_context=None):
                return {'replacing_all_the_content': 'yes'}
        parser_classes = (TestParser,)
        def post(self, request, *args, **kwargs):
            return Response(request.DATA)
    self.view = TestView.as_view()

def test_form_parser_called(self):
    request = factory.post('/', {'foo': 'bar'})
    response = self.view(request)
    self.assertEquals(response.data, {'replacing_all_the_content': 'yes'})

class TestJsonParsers(TestCase):

def setUp(self):
    class TestView(APIView):
        class TestParser(parsers.JSONParser):
            def parse(self, stream, media_type=None, parser_context=None):
                return {'replacing_all_the_content': 'yes'}
        parser_classes = (TestParser,)
        def post(self, request, *args, **kwargs):
            return Response(request.DATA)
    self.view = TestView.as_view()

def test_form_parser_called(self):
    request = factory.post('/', {'foo':'bar'}, format='json')
    response = self.view(request)
    self.assertEquals(response.data, {'replacing_all_the_content': 'yes'})

I tried for a while but couldn't come up with a working fix.