You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django视图与pytest测试数据解析矛盾问题求助

解决Django API测试中string indices must be integers的矛盾错误

我一眼就看出问题出在测试用例的请求格式上——你正常运行时应该是用JSON格式发送请求(比如Postman、前端Axios),但pytest测试里用client.post(url, data)时,默认是把数据以**表单编码(form-encoded)**的方式发送,这会让Django视图接收到的request.data结构完全走样!

问题根源拆解

  • 正常请求时:request.data['nodes']是你预期的字典数组[{'name': '25', ...}, ...],遍历每个data_node都是字典,所以data_node['name']完全没问题。
  • 测试请求时:表单编码会把复杂结构(比如数组、嵌套字典)解析成字符串或者扁平化的键值对,导致nodes里的元素变成了字符串,遍历的时候data_node是字符串,自然就会报string indices must be integers——你总不能用字符串当索引去取字典值吧?

而你后来改成int(data_node['name'])时,正常环境下data_node['name']是字符串"25",转int没问题,但测试环境下data_node本身就是字符串(比如可能是"name"或者乱码的内容),这时候data_node['name']本身就报错,或者取到的不是合法字符串,才会出现int() argument must be a string的矛盾错误。

直接解决:修正测试用例的请求格式

你需要让测试用例发送JSON格式的请求,而不是表单编码。有两种方式:

方式1:Django 3.1+ 简化写法

直接用json参数传递数据,测试client会自动帮你转成JSON并设置正确的Content-Type:

def test_aut_user_can_create_graph(self, client):
    data = {'project': 'project1', 'name': 'Graph1', 'description': 'test', 
            'nodes': [{'name': '25', 'graph': 3}, {'name': '24', 'graph': 3}], 
            'edges': [{'name': 'EdgeForGraph1', 'graph': 3, 'source': '25', 'target': '25'}]}
    url = api_reverse('nameofurl')
    response = client.post(url, json=data)
    assert response.status_code == 201

方式2:兼容旧版本Django

手动把数据转成JSON字符串,指定content_type:

import json

def test_aut_user_can_create_graph(self, client):
    data = {'project': 'project1', 'name': 'Graph1', 'description': 'test', 
            'nodes': [{'name': '25', 'graph': 3}, {'name': '24', 'graph': 3}], 
            'edges': [{'name': 'EdgeForGraph1', 'graph': 3, 'source': '25', 'target': '25'}]}
    url = api_reverse('nameofurl')
    response = client.post(url, json.dumps(data), content_type='application/json')
    assert response.status_code == 201

进阶优化:给视图加数据校验(推荐)

为了避免以后再踩类似的格式坑,建议用DRF的序列化器来验证请求数据的结构,这样不管是测试还是生产环境,只要数据格式不对,都会返回清晰的错误信息:

from rest_framework import serializers, status

class NodeSerializer(serializers.Serializer):
    name = serializers.CharField()
    graph = serializers.IntegerField()

class GraphCreateSerializer(serializers.Serializer):
    project = serializers.CharField()
    name = serializers.CharField()
    description = serializers.CharField()
    nodes = NodeSerializer(many=True)
    edges = serializers.ListField(child=serializers.DictField())

@api_view(['POST'])
def CreateGraphView(request):
    serializer = GraphCreateSerializer(data=request.data)
    if serializer.is_valid():
        nodes = serializer.validated_data['nodes']
        for data_node in nodes:
            node_name = data_node['name']
            new_node = Node.objects.create(name=node_name)
        return Response(status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

这样以后如果再出现数据格式错误,你能直接从返回的serializer.errors里看到具体哪里错了,不用再瞎猜啦!

内容的提问来源于stack exchange,提问作者Micromegas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:33:09