Django Rest Framework APIRequestFactory request ob

2020-02-26 08:25发布

问题:

Lets say I have this APIView

class Dummy(APIView):
    def get(self, request):
        return Response(data=request.query_params.get('uuid'))

To test it, I need to create a request object to pass into the get function

def test_dummy(self):
    from rest_framework.test import APIRequestFactory
    factory = APIRequestFactory()
    request = factory.get('/?uuid=abcd')
    DummyView().get(request)

It complains about AttributeError: 'WSGIRequest' object has no attribute 'query_params'

Having a closer look, the factory creates a WSGIRequest instance instead of a DRF version <class 'rest_framework.request.Request'>.

>>> from rest_framework.test import APIRequestFactory
>>> factory = APIRequestFactory()
>>> request = factory.get('/')
>>> request.__class__
<class 'django.core.handlers.wsgi.WSGIRequest'>

回答1:

That's right. At the moment APIRequestFactory returns an HttpRequest object, which only get upgraded to a REST framework Request object once it gets to the view layer.

This mirrors the behavior that you'll see in an actual request, and what it does do is deal with eg. rendering JSON, XML or whatever other content type you have configured for your test requests.

However I agree that it's surprising behavior and at some point it will probably return a Request object, and the REST framework view will ensure that it only performs the Request upgrade on requests that instances of HttpRequest.

What you need to do in your case is actually call the view, rather than invoking the .get() method...

factory = APIRequestFactory()
request = factory.get('/?uuid=abcd')
view = DummyView.as_view()
response = view(request)  # Calling the view, not calling `.get()`


回答2:

Refer to Tom's solution, DummyView()(request) will raise error:

TypeError: 'DummyView' object is not callable

Instead, should use as_view just like what you do in urls.py:

DummyView.as_view()(request)

DRF's as_view uses method initialize_request to convert Django Request object to DRF version. You can try with:

from rest_framework.views import APIView
APIView().initialize_request(request)
>>> <rest_framework.request.Request object at 0xad9850c>

You can also use APIClient to run test. It also tests URL dispatching.

from rest_framework.test import APIClient
client = APIClient()
client.post('/notes/', {'title': 'new idea'}, format='json')


回答3:

I realize this answer is some time after the question was asked, but it solved this issue for me. Just override the APIRequestFactory class as below.

# Override APIRequestFactory to add the query_params attribute.
class MyAPIRequestFactory(APIRequestFactory):

    def generic(self, method, path, data='',
                content_type='application/octet-stream', secure=False,
                **extra):
        # Include the CONTENT_TYPE, regardless of whether or not data is empty.
        if content_type is not None:
            extra['CONTENT_TYPE'] = str(content_type)

        request = super(MyAPIRequestFactory, self).generic(
            method, path, data, content_type, secure, **extra)
        request.query_params = request.GET
        return request