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

Django测试APIView的patch方法时查询参数为空报MultiValueDictKeyError

问题根因

Django REST Framework的测试客户端APITestCase.client的patch方法不支持query_params入参,你传入的该参数不会被自动拼接到URL的查询字符串中,因此后端的request.query_params为空,才会抛出MultiValueDictKeyError。前端axios能正常调用是因为你发起请求时主动把money_code拼到了URL的查询串部分。

解决方案

方案1:仅修改测试用例,不改动现有业务逻辑

手动把查询参数拼接到请求URL后即可:

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase

class MoneyUpdateTest(APITestCase):
    def test_update_quantity(self):
        """
        Ensure we can update the quantity.
        """
        obj = Money.objects.create(
            money_code="RG100TEST1",
            supplier="Test supplier",
        )
        # 拼接查询参数到URL
        base_url = reverse("update-money-quantity")
        url = f"{base_url}?money_code=RG100TEST1"
        data = {
            "saving": 110,
            "actual_saving": 105,
        }
        # 不需要再传query_params参数
        response = self.client.patch(url, data=data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            Money.objects.get(money_code="RG100TEST1").saving, 110
        )
        self.assertEqual(
            Money.objects.get(money_code="RG100TEST1").actual_saving, 105
        )

如果查询参数较多,也可以用QueryDict批量编码拼接:

from django.http import QueryDict

params = {"money_code": "RG100TEST1", "other_param": "xxx"}
query_str = QueryDict(mutable=True)
query_str.update(params)
url = f"{reverse('update-money-quantity')}?{query_str.urlencode()}"

方案2:优化接口设计,符合REST规范(更推荐)

资源唯一标识放在URL路径中是REST API的标准设计,也能避免查询参数传递的各类问题:

  1. 修改路由配置
path(
    # 把money_code作为路径参数
    "update-money-quantity/<str:money_code>/",
    UpdateMoneyQuantity.as_view(),
    name="update-money-quantity",
),
  1. 修改视图代码
class UpdateMoneyQuantity(APIView):
    # 直接从路径获取money_code参数
    def patch(self, request, money_code):
        try:
            # 删掉原来从query_params取参数的代码,直接用路径传入的money_code
            money_object = Money.objects.get(money_code=money_code)
            # 后续逻辑保持不变
            serializer = MoneySerializer(
                money_object, data=request.data, partial=True
            )
            if serializer.is_valid():
                serializer.save()
                return Response(data=serializer.data, status=HTTP_200_OK)
            return Response(data=serializer.errors, status=HTTP_400_BAD_REQUEST)
        except Money.DoesNotExist as err:
            return Response(
                data={
                    "error": "Unable to find the desired code."
                },
                status=HTTP_400_BAD_REQUEST,
            )
        except Exception as err:
            logger.exception(
                f"Unable to perform patch on money quantity. \n Exception: {str(err)}"
            )
            return Response(data=str(err), status=HTTP_500_INTERNAL_SERVER_ERROR)
  1. 修改测试用例
class MoneyUpdateTest(APITestCase):
    def test_update_quantity(self):
        """
        Ensure we can update the quantity.
        """
        obj = Money.objects.create(
            money_code="RG100TEST1",
            supplier="Test supplier",
        )
        # reverse时传入路径参数
        url = reverse("update-money-quantity", kwargs={"money_code": "RG100TEST1"})
        data = {
            "saving": 110,
            "actual_saving": 105,
        }
        response = self.client.patch(url, data=data)
        self.assertEqual(response.status_code, HTTP_200_OK)
        self.assertEqual(
            Money.objects.get(money_code="RG100TEST1").saving, 110
        )
        self.assertEqual(
            Money.objects.get(money_code="RG100TEST1").actual_saving, 105
        )

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 10:15:03