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的标准设计,也能避免查询参数传递的各类问题:
- 修改路由配置
path( # 把money_code作为路径参数 "update-money-quantity/<str:money_code>/", UpdateMoneyQuantity.as_view(), name="update-money-quantity", ),
- 修改视图代码
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)
- 修改测试用例
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
相关产品推荐
相关产品推荐

