Python Django请求数据返回str而不是list

Python Django请求数据返回str而不是list,python,django,django-rest-framework,Python,Django,Django Rest Framework,我正在用Django和REST框架开发RESTAPI。我有一个端点,它使用这种json接收POST请求: { "pipeline": ["Bayes"], "material": [ "Rakastan iloisuutta!", "Autojen kanssa pitää olla varovainen.", "Paska kesä taas. Kylmää ja sataa"

我正在用Django和REST框架开发RESTAPI。我有一个端点,它使用这种json接收POST请求:

{
        "pipeline": ["Bayes"],
        "material": [
            "Rakastan iloisuutta!",
            "Autojen kanssa pitää olla varovainen.",
            "Paska kesä taas. Kylmää ja sataa"
        ]
    }
这是一个机器学习分析api,json告诉我们使用贝叶斯分类器来提供字符串并返回结果。当我通过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.")
在本例中,给出我请求中列表的最后一个条目

“Paska kesätaas.Kylmäja sataa”

然而,当我检查request.data的内容时,它显示了一个querydict,其中列出了请求中的管道和材料。为什么在调用request.data[“material”]时获取字符串而不是物料列表?是否有什么我忘记了,我必须指定某种序列化程序?为什么它在正常执行期间工作,但在测试中不工作

我在Python3中使用Django 1.8。此外,我没有将视图绑定到任何特定的模型

最后,当我将断点放入视图时,我的调试器显示了以下内容: 请求.数据:

QueryDict: {'material': ['Rakastan iloisuutta!', 'Autojen kanssa pitää olla varovainen.', 'Paska kesä taas. Kylmää ja sataa'], 'pipeline': ['Bayes']}
asd=请求。数据[“物料”]:


在测试中尝试这样做:

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在
\uuuu getitem\uuuu
中返回列表的最后一个值:

QueryDict.getitem(键)

返回给定键的值。如果键有多个值,getitem()返回最后一个值。如果键不存在,则引发django.utils.datastructures.MultiValue DictKeyError。(这是Python标准KeyError的一个子类,因此您可以坚持捕获KeyError。)

如果发布一个表单,其中键映射到列表:

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
由于测试方法将数据作为表单发布,因此从request.data获得的是QueryDict(与request.post相同),因此在获取request.data时,您将获得列表中的最后一个值


要获得预期的行为,请将数据作为JSON发布到请求正文中(如@Vladir Parrado Cruz的回答)

默认情况下,在执行
getitem
调用(或通过方括号进行访问,如您在
request.data['material']
中所做的操作)时,QueryDict将从列表中返回单个项

您可以使用
getlist
方法返回键的所有值:

class ClassifyText(APIView):
"""
将文本片段作为参数并返回分析结果。
"""
身份验证\类=(authentication.TokenAuthentication,)
权限\类=(permissions.AllowAny,)
解析器\类=(JSONParser,)
def post(自我、请求、格式=无):
尝试:
自我验证发布数据(request.data)
打印(“juttu”,请求。数据[“材料”])
打印(“juttu”,request.data.getlist(“material”]))
#从请求创建管道
管道=管道()
对于request.data[“pipeline”]中的组件名称:
管道。添加组件(组件名称)
response=pipeline.execute_pipeline(request.data.getlist('material'))
状态代码=status.HTTP\u 200\u正常
例外情况除外,如e:
响应={“消息”:“请提供正确的数据。”,
“错误”:str(e)}
status\u code=status.HTTP\u 400\u错误请求
返回响应(响应,状态=状态\代码)
定义验证后数据(自身、数据):
如果“管道”不在数据中:
raise InvalidRequest(“缺少管道字段。应为分析中使用的组件数组。可在/api/classifiers处找到组件”)
如果len(数据[“管道])<1:
raise InvalidRequest(“管道数组为空”)
如果“材料”不在数据中:
raise InvalidRequest(“缺少要分析的材料。请提供字符串数组。”)
如果len(数据[“材料])<1:
raise InvalidRequest(“缺少要分析的材料,数组为空。请提供字符串数组。”)

Pipeline.execute\u Pipeline做什么?它处理材料。然而,我相当确定问题是在这之前,因为调试程序向我显示,在测试期间,execute_管道实际上获取字符串而不是列表(它应该获取列表)。另外,如果我创建了一个类似asd=request.data[“material”]的表达式,那么根据调试器,asd包含列表的最后一个条目(字符串)。如果手动测试,它不会得到它吗?当这两个示例(打印)行位于彼此下方时,调试器输出是什么?感谢您的解释。看起来昨天晚上我浏览文档时太累了。我最终使用了@Vladir Parrado Cruz的答案。
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
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.")