Django 请求数据 returns str 而不是 list
Django request data returns str instead of list
我正在使用 Django 和 REST 框架开发 REST api。我有一个接受 POST 请求的端点,这种 json:
{
"pipeline": ["Bayes"],
"material": [
"Rakastan iloisuutta!",
"Autojen kanssa pitää olla varovainen.",
"Paska kesä taas. Kylmää ja sataa"
]
}
这是一个机器学习分析api,json告诉我们使用贝叶斯分类器来提供字符串和return结果。当我通过执行 post 请求手动测试它时,这工作正常。但是,当我尝试编写单元测试时它崩溃了。我有以下测试:
class ClassifyTextAPITests(APITestCase):
fixtures = ['fixtures/analyzerfixtures.json'] #suboptimal fixture since requires bayes.pkl in /assets/classifiers folder
def test_classification(self):
""" Make sure that the API will respond correctly when required url params are supplied.
"""
response = self.client.post(reverse('analyzer_api:classifytext'), {
"pipeline": ["Bayes"],
"material": [
"Rakastan iloisuutta!",
"Autojen kanssa pitää olla varovainen.",
"Paska kesä taas. Kylmää ja sataa",
]
})
self.assertTrue(status.is_success(response.status_code))
self.assertEqual(response.data[0], 1)
测试每次都失败,因为后一个断言给出 "AssertionError: 'P' != 1"
这是我的查看代码:
class ClassifyText(APIView):
"""
Takes text snippet as a parameter and returns analyzed result.
"""
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,)
parser_classes = (JSONParser,)
def post(self, request, format=None):
try:
self._validate_post_data(request.data)
print("juttu", request.data["material"])
#create pipeline from request
pipeline = Pipeline()
for component_name in request.data["pipeline"]:
pipeline.add_component(component_name)
response = pipeline.execute_pipeline(request.data['material'])
status_code = status.HTTP_200_OK
except Exception as e:
response = {"message": "Please provide a proper data.",
"error": str(e) }
status_code = status.HTTP_400_BAD_REQUEST
return Response(response, status=status_code)
def _validate_post_data(self, data):
if "pipeline" not in data:
raise InvalidRequest("Pipeline field is missing. Should be array of components used in analysis. Available components at /api/classifiers")
if len(data["pipeline"]) < 1:
raise InvalidRequest("Pipeline array is empty.")
if "material" not in data:
raise InvalidRequest("Material to be analyzed is missing. Please provide an array of strings.")
if len(data["material"]) < 1:
raise InvalidRequest("Material to be analyzed is missing, array is empty. Please provide an array of strings.")
真正有趣的部分是我启动调试器来检查这里发生了什么。结果是行
request.data['material']
在我的请求中给出列表的最后一个条目,在本例中为
"Paska kesä taas. Kylmää ja sataa"
然而,当我检查 request.data 的内容时,它显示了一个带有列表管道和 material 的查询字典,因为它们是请求的。为什么我在调用 request.data["material"] 时得到的是字符串而不是 material 列表?有没有我忘记的东西,我必须指定某种序列化程序?以及为什么它在正常执行期间有效但在测试中无效?
我将 Django 1.8 与 Python 3 一起使用。此外,我没有将视图绑定到任何特定模型。
最后,这是我的调试器在我将断点置于视图中时显示的内容:
request.data:
QueryDict: {'material': ['Rakastan iloisuutta!', 'Autojen kanssa pitää olla varovainen.', 'Paska kesä taas. Kylmää ja sataa'], 'pipeline': ['Bayes']}
asd = request.data["material"]:
'Paska kesä taas. Kylmää ja sataa'
尝试在测试中做类似的事情:
import json
def test_classification(self):
""" Make sure that the API will respond correctly when required url params are supplied.
"""
response = self.client.post(
reverse('analyzer_api:classifytext'),
json.dumps({
"pipeline": ["Bayes"],
"material": [
"Rakastan iloisuutta!",
"Autojen kanssa pitää olla varovainen.",
"Paska kesä taas. Kylmää ja sataa",
]
}),
content_type='application/json'
)
self.assertTrue(status.is_success(response.status_code))
self.assertEqual(response.data[0], 1)
如果您将数据作为 json 发送,也许它会起作用。
这是因为 QueryDict returns __getitem__
中列表的最后一个值:
QueryDict.getitem(key)
Returns the value for the given key. If the key has more than one value, getitem() returns the last value. Raises django.utils.datastructures.MultiValueDictKeyError if the key does not exist. (This is a subclass of Python’s standard KeyError, so you can stick to catching KeyError.)
https://docs.djangoproject.com/en/1.8/ref/request-response/#django.http.QueryDict.getitem
如果您 post 一个表单,其中一个键映射到一个列表:
d = {"a": 123, "b": [1,2,3]}
requests.post("http://127.0.0.1:6666", data=d)
这是您在请求正文中得到的内容:
a=123&b=1&b=2&b=3
由于测试方法post数据是一个表单,你从request.data得到的是一个QueryDict(与request.POST相同),因此你得到最后一个值获取 request.data 时的列表。
为了获得预期的行为,post 请求正文中的数据 JSON(如@Vladir Parrado Cruz 的回答)。
默认情况下,QueryDict 将在执行 getitem
调用时 return 列表中的单个项目(或通过方括号访问,例如您在 request.data['material']
中所做的)
您可以改为使用 getlist
方法 return 键的所有值:
https://docs.djangoproject.com/en/1.8/ref/request-response/#django.http.QueryDict.getlist
class ClassifyText(APIView):
"""
Takes text snippet as a parameter and returns analyzed result.
"""
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,)
parser_classes = (JSONParser,)
def post(self, request, format=None):
try:
self._validate_post_data(request.data)
print("juttu", request.data["material"])
print("juttu", request.data.getlist("material"]))
#create pipeline from request
pipeline = Pipeline()
for component_name in request.data["pipeline"]:
pipeline.add_component(component_name)
response = pipeline.execute_pipeline(request.data.getlist('material'))
status_code = status.HTTP_200_OK
except Exception as e:
response = {"message": "Please provide a proper data.",
"error": str(e) }
status_code = status.HTTP_400_BAD_REQUEST
return Response(response, status=status_code)
def _validate_post_data(self, data):
if "pipeline" not in data:
raise InvalidRequest("Pipeline field is missing. Should be array of components used in analysis. Available components at /api/classifiers")
if len(data["pipeline"]) < 1:
raise InvalidRequest("Pipeline array is empty.")
if "material" not in data:
raise InvalidRequest("Material to be analyzed is missing. Please provide an array of strings.")
if len(data["material"]) < 1:
raise InvalidRequest("Material to be analyzed is missing, array is empty. Please provide an array of strings.")
我正在使用 Django 和 REST 框架开发 REST api。我有一个接受 POST 请求的端点,这种 json:
{
"pipeline": ["Bayes"],
"material": [
"Rakastan iloisuutta!",
"Autojen kanssa pitää olla varovainen.",
"Paska kesä taas. Kylmää ja sataa"
]
}
这是一个机器学习分析api,json告诉我们使用贝叶斯分类器来提供字符串和return结果。当我通过执行 post 请求手动测试它时,这工作正常。但是,当我尝试编写单元测试时它崩溃了。我有以下测试:
class ClassifyTextAPITests(APITestCase):
fixtures = ['fixtures/analyzerfixtures.json'] #suboptimal fixture since requires bayes.pkl in /assets/classifiers folder
def test_classification(self):
""" Make sure that the API will respond correctly when required url params are supplied.
"""
response = self.client.post(reverse('analyzer_api:classifytext'), {
"pipeline": ["Bayes"],
"material": [
"Rakastan iloisuutta!",
"Autojen kanssa pitää olla varovainen.",
"Paska kesä taas. Kylmää ja sataa",
]
})
self.assertTrue(status.is_success(response.status_code))
self.assertEqual(response.data[0], 1)
测试每次都失败,因为后一个断言给出 "AssertionError: 'P' != 1"
这是我的查看代码:
class ClassifyText(APIView):
"""
Takes text snippet as a parameter and returns analyzed result.
"""
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,)
parser_classes = (JSONParser,)
def post(self, request, format=None):
try:
self._validate_post_data(request.data)
print("juttu", request.data["material"])
#create pipeline from request
pipeline = Pipeline()
for component_name in request.data["pipeline"]:
pipeline.add_component(component_name)
response = pipeline.execute_pipeline(request.data['material'])
status_code = status.HTTP_200_OK
except Exception as e:
response = {"message": "Please provide a proper data.",
"error": str(e) }
status_code = status.HTTP_400_BAD_REQUEST
return Response(response, status=status_code)
def _validate_post_data(self, data):
if "pipeline" not in data:
raise InvalidRequest("Pipeline field is missing. Should be array of components used in analysis. Available components at /api/classifiers")
if len(data["pipeline"]) < 1:
raise InvalidRequest("Pipeline array is empty.")
if "material" not in data:
raise InvalidRequest("Material to be analyzed is missing. Please provide an array of strings.")
if len(data["material"]) < 1:
raise InvalidRequest("Material to be analyzed is missing, array is empty. Please provide an array of strings.")
真正有趣的部分是我启动调试器来检查这里发生了什么。结果是行
request.data['material']
在我的请求中给出列表的最后一个条目,在本例中为
"Paska kesä taas. Kylmää ja sataa"
然而,当我检查 request.data 的内容时,它显示了一个带有列表管道和 material 的查询字典,因为它们是请求的。为什么我在调用 request.data["material"] 时得到的是字符串而不是 material 列表?有没有我忘记的东西,我必须指定某种序列化程序?以及为什么它在正常执行期间有效但在测试中无效?
我将 Django 1.8 与 Python 3 一起使用。此外,我没有将视图绑定到任何特定模型。
最后,这是我的调试器在我将断点置于视图中时显示的内容: request.data:
QueryDict: {'material': ['Rakastan iloisuutta!', 'Autojen kanssa pitää olla varovainen.', 'Paska kesä taas. Kylmää ja sataa'], 'pipeline': ['Bayes']}
asd = request.data["material"]:
'Paska kesä taas. Kylmää ja sataa'
尝试在测试中做类似的事情:
import json
def test_classification(self):
""" Make sure that the API will respond correctly when required url params are supplied.
"""
response = self.client.post(
reverse('analyzer_api:classifytext'),
json.dumps({
"pipeline": ["Bayes"],
"material": [
"Rakastan iloisuutta!",
"Autojen kanssa pitää olla varovainen.",
"Paska kesä taas. Kylmää ja sataa",
]
}),
content_type='application/json'
)
self.assertTrue(status.is_success(response.status_code))
self.assertEqual(response.data[0], 1)
如果您将数据作为 json 发送,也许它会起作用。
这是因为 QueryDict returns __getitem__
中列表的最后一个值:
QueryDict.getitem(key)
Returns the value for the given key. If the key has more than one value, getitem() returns the last value. Raises django.utils.datastructures.MultiValueDictKeyError if the key does not exist. (This is a subclass of Python’s standard KeyError, so you can stick to catching KeyError.)
https://docs.djangoproject.com/en/1.8/ref/request-response/#django.http.QueryDict.getitem
如果您 post 一个表单,其中一个键映射到一个列表:
d = {"a": 123, "b": [1,2,3]}
requests.post("http://127.0.0.1:6666", data=d)
这是您在请求正文中得到的内容:
a=123&b=1&b=2&b=3
由于测试方法post数据是一个表单,你从request.data得到的是一个QueryDict(与request.POST相同),因此你得到最后一个值获取 request.data 时的列表。
为了获得预期的行为,post 请求正文中的数据 JSON(如@Vladir Parrado Cruz 的回答)。
默认情况下,QueryDict 将在执行 getitem
调用时 return 列表中的单个项目(或通过方括号访问,例如您在 request.data['material']
中所做的)
您可以改为使用 getlist
方法 return 键的所有值:
https://docs.djangoproject.com/en/1.8/ref/request-response/#django.http.QueryDict.getlist
class ClassifyText(APIView):
"""
Takes text snippet as a parameter and returns analyzed result.
"""
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,)
parser_classes = (JSONParser,)
def post(self, request, format=None):
try:
self._validate_post_data(request.data)
print("juttu", request.data["material"])
print("juttu", request.data.getlist("material"]))
#create pipeline from request
pipeline = Pipeline()
for component_name in request.data["pipeline"]:
pipeline.add_component(component_name)
response = pipeline.execute_pipeline(request.data.getlist('material'))
status_code = status.HTTP_200_OK
except Exception as e:
response = {"message": "Please provide a proper data.",
"error": str(e) }
status_code = status.HTTP_400_BAD_REQUEST
return Response(response, status=status_code)
def _validate_post_data(self, data):
if "pipeline" not in data:
raise InvalidRequest("Pipeline field is missing. Should be array of components used in analysis. Available components at /api/classifiers")
if len(data["pipeline"]) < 1:
raise InvalidRequest("Pipeline array is empty.")
if "material" not in data:
raise InvalidRequest("Material to be analyzed is missing. Please provide an array of strings.")
if len(data["material"]) < 1:
raise InvalidRequest("Material to be analyzed is missing, array is empty. Please provide an array of strings.")