Django Rest Framework APIRequestFactory 请求对象没有属性'query_params'

Django Rest Framework APIRequestFactory request object has no attribute 'query_params'

假设我有这个 APIView

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

为了测试它,我需要创建一个请求对象以传递给 get 函数

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

它抱怨 AttributeError: 'WSGIRequest' object has no attribute 'query_params'

仔细观察,工厂创建了一个 WSGIRequest 实例而不是 DRF 版本 <class 'rest_framework.request.Request'>

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

没错。目前 APIRequestFactory return 是一个 HttpRequest 对象,只有在到达视图层后才会升级为 REST 框架 Request 对象。

这反映了您将在实际请求中看到的行为,它所做的 所做的是处理例如。呈现 JSON、XML 或您为测试请求配置的任何其他内容类型。

但是我同意这是令人惊讶的行为,在某些时候它可能会 return 一个 Request 对象,并且 REST 框架视图将确保它只执行 Request 升级根据 HttpRequest.

实例的请求

你需要做的是实际调用视图,而不是调用.get()方法...

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

参考Tom的解决方案,DummyView()(request)会报错:

TypeError: 'DummyView' object is not callable

相反,应该像在 urls.py 中那样使用 as_view:

DummyView.as_view()(request)

DRF 的 as_view 使用 method initialize_request 将 Django Request 对象转换为 DRF 版本。你可以试试:

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

您也可以使用APIClient 来运行 测试。它还测试 URL 调度。

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

我意识到这个答案是在问题被问到之后的一段时间,但它为我解决了这个问题。只需覆盖 APIRequestFactory class 如下。

# 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