Django DRF测试query_params报AttributeError如何解决
如何正确测试依赖query_params的序列化器逻辑
报错复现
测试序列化器中读取request.query_params计算payment字段的逻辑时,先后触发两类报错:
- 初始报错
price = int(self.context.get('request').query_params.get('price', None)) AttributeError: 'NoneType' object has no attribute 'query_params'
- 调整传参后报错
File "D:\Work\Django\Test_ipoteka\ipoteka\serializers.py", line 22, in get_payment price = int(self.context.get('request').query_params.get('price', None)) AttributeError: 'Response' object has no attribute 'query_params'
移除context中的response字段后问题仍未解决。
原有问题代码
test_api.py
class IpotekaApiTestCase(APITestCase): def setUp(self) -> None: self.offer_1 = Ipoteka.objects.create(bank_name='test', term_min=1, term_max=3, rate_min=1.5, rate_max=4.8, payment_min=12345, payment_max=123456) self.offer_2 = Ipoteka.objects.create(bank_name='test_1', term_min=1, term_max=3, rate_min=1.5, rate_max=4.8, payment_min=12345, payment_max=123456) def test_get(self): url = reverse('offer-list') response = self.client.get(url) view = IpotekaModelViewSet.as_view({'get': 'list'}) request = RequestFactory().get('/') context = {'request': view(request), 'response': response} serializer_data = IpotekaSerializer([self.offer_1, self.offer_2], context=context, many=True).data self.assertEqual(status.HTTP_200_OK, response.status_code) self.assertEqual(serializer_data, response.data['results'])
serializers.py
def get_payment(self, obj) -> int: try: price = int(self.context.get('request').query_params.get('price', None)) deposit = int(self.context.get('request').query_params.get('deposit', None)) term = int(self.context.get('request').query_params.get('term', None)) if price == 0 or term == 0: return 0 return self._get_offer_result(price, deposit, term, obj) except (ValueError, TypeError): return 0
报错根因
构造序列化器context的逻辑存在三个核心错误:
query_params是DRF Request 对象的专属属性,第一次传值时request对应位置取值为None,第二次误将视图执行返回的Response对象传给了request键,两类对象都没有query_params属性,必然触发报错- 直接用Django原生
RequestFactory生成的是Django原生HttpRequest实例,本身不携带query_params属性,必须经过DRF的Request类封装后才能被序列化器正常识别 - 构造请求时没有传入price、deposit、term这几个测试用的query参数,即使request构造正确也无法覆盖目标计算逻辑
正确实现方案
方案1(推荐):直接通过测试客户端发起带参请求
不需要手动实例化序列化器,完全模拟真实接口调用场景,写法更简洁,测试覆盖更贴近线上实际逻辑:
from rest_framework.test import APITestCase from django.urls import reverse from rest_framework import status from yourapp.models import Ipoteka class IpotekaApiTestCase(APITestCase): def setUp(self) -> None: self.offer_1 = Ipoteka.objects.create(bank_name='test', term_min=1, term_max=3, rate_min=1.5, rate_max=4.8, payment_min=12345, payment_max=123456) self.offer_2 = Ipoteka.objects.create(bank_name='test_1', term_min=1, term_max=3, rate_min=1.5, rate_max=4.8, payment_min=12345, payment_max=123456) def test_get_with_query_params(self): url = reverse('offer-list') # 直接在get请求中传入需要测试的query参数 test_params = { 'price': 1000000, 'deposit': 200000, 'term': 20 } response = self.client.get(url, test_params) self.assertEqual(status.HTTP_200_OK, response.status_code) # 直接校验返回结果即可,不需要手动构造序列化器 for result in response.data['results']: self.assertIn('payment', result) self.assertIsInstance(result['payment'], int) # 可补充payment计算结果的具体断言
方案2:手动构造合法context传入序列化器
如果需要单独对序列化器做单元测试,按如下方式构造合法的DRF Request对象传入context即可:
from rest_framework.request import Request from django.test import RequestFactory from yourapp.serializers import IpotekaSerializer def test_serializer_payment(self): factory = RequestFactory() # 构造原生Django请求时带上测试参数 django_request = factory.get('/', data={ 'price': 1000000, 'deposit': 200000, 'term': 20 }) # 封装为DRF可识别的Request对象 drf_request = Request(django_request) context = {'request': drf_request} serializer_data = IpotekaSerializer( [self.offer_1, self.offer_2], context=context, many=True ).data # 补充序列化结果断言
注意:context里的request键对应值必须是DRF的Request实例,不能传入视图返回的Response对象。
内容的提问来源于stack exchange,提问作者Ramil
相关产品推荐
相关产品推荐

