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
假设我有这个 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