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

Django DRF测试query_params报AttributeError如何解决

如何正确测试依赖query_params的序列化器逻辑

报错复现

测试序列化器中读取request.query_params计算payment字段的逻辑时,先后触发两类报错:

  1. 初始报错
price = int(self.context.get('request').query_params.get('price', None))
AttributeError: 'NoneType' object has no attribute 'query_params'
  1. 调整传参后报错
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:27:23