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

Django表单单元测试报KeyError: 'request',请求排查解决

问题:ModelForm单元测试报KeyError: 'request'

重写了ModelForm的__init__方法来获取kwargs里的request,业务场景中运行正常,但单元测试时出现KeyError: 'request'。表单用于检测同一用户下的课程名称重复,测试用例已创建用户和Course对象。

相关代码

表单代码

class CourseForm(ModelForm):
    """ Form used for both creating a new course or updating an existing course."""
    class Meta:
        model = Course
        fields = ('course_name', 'grade_level',)

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        self.qc = Course.objects.filter(user=self.request.user)
        super().__init__(*args, **kwargs)

    def clean(self):
        super(CourseForm, self).clean()
        course_name = self.cleaned_data.get('course_name')
        grade_level = self.cleaned_data.get('grade_level')
        # check exising course_name per user
        # exclude(pk=self.instance.pk) because when we update we don't have to consider the current name
        if course_name and self.qc.exclude(pk=self.instance.pk).filter(course_name__iexact=course_name).exists():
            raise ValidationError("A course with that name already exists.")

        return self.cleaned_data

测试用例代码

class CourseFormTests(TestCase):
    @classmethod
    def setUp(self):
        self.user = CustomUser.objects.create_user(
            username='tester',
            email='tester@email.com',
            password='tester123',
            is_teacher=True,
            is_active=True,
        )
        self.user.save()
        self.my_course = Course(user=self.user,
                                id='4d192045-07fa-477f-bac2-5a99fe2e7c04',
                                course_name="name",
                                grade_level="SEC")
        self.my_course.save()

    def test_CourseForm_valid(self):
        form = CourseForm(self.user, data={
            'user': self.user,
            'id': '4d192045-07fa-477f-bac2-5a99fe2e7c04',
            'course_name': "Science",
            'grade_level': "SEC"
        },)
        self.assertTrue(form.is_valid())

    def test_CourseForm_invalid_name_too_long(self):
        form = CourseForm(self.user, data={
            'user': self.user,
            'id': '4d192045-07fa-477f-bac2-5a99fe2e7c05',
            'course_name': "NameistoolongforthistobeOKNameistoolongforthistobeOK",
            'grade_level': "SEC"
        },)
        self.assertFalse(form.is_valid())

    def test_CourseForm_invalid_name_exists(self):
        form = CourseForm(self.user, data={
            'user': self.user,
            'id': '4d192045-07fa-477f-bac2-5a99fe2e7c05',
            'course_name': "name",
            'grade_level': "SEC"
        },)
        self.assertFalse(form.is_valid())

视图调用代码

def courses(request):
    """view for listing and creating a course"""
    course_list = Course.objects.filter(
        user=request.user).order_by('course_name')
    context = {'course_list': course_list}
    user = request.user

    if request.method == 'POST':
        details = CourseForm(request.POST, request=request)

        if details.is_valid():
            if user.username != "Demo":
                course = details.save(commit=False)
                course.user = user
                course.save()

            form = CourseForm(request=request)
            context['form'] = form
            return render(request, "gradebook/courses.html", context)

        else:
            context['form'] = details
            return render(request, "gradebook/courses.html", context)

    else:
        form = CourseForm(request=request)
        context['form'] = form
        return render(request, "gradebook/courses.html", context)

# urls.py
path('courses/', views.courses, name='courses'),

报错信息

line 107, in __init__
    self.request = kwargs.pop('request')
KeyError: 'request'

解决方案

1. 修正测试用例的表单实例化方式

表单的__init__要求从关键字参数中获取request,但测试用例里把self.user作为第一个位置参数传入,不符合调用约定。需要创建模拟request对象,以request=模拟request的关键字参数传入。

修改后的测试用例示例:

from django.test import RequestFactory

class CourseFormTests(TestCase):
    @classmethod
    def setUp(self):
        self.factory = RequestFactory()
        self.user = CustomUser.objects.create_user(
            username='tester',
            email='tester@email.com',
            password='tester123',
            is_teacher=True,
            is_active=True,
        )
        self.my_course = Course.objects.create(
            user=self.user,
            id='4d192045-07fa-477f-bac2-5a99fe2e7c04',
            course_name="name",
            grade_level="SEC"
        )

    def test_CourseForm_valid(self):
        # 创建模拟request并设置用户
        request = self.factory.get('/')
        request.user = self.user
        # 正确传入request关键字参数
        form = CourseForm(data={
            'course_name': "Science",
            'grade_level': "SEC"
        }, request=request)
        self.assertTrue(form.is_valid())

    def test_CourseForm_invalid_name_exists(self):
        request = self.factory.get('/')
        request.user = self.user
        form = CourseForm(data={
            'course_name': "name",
            'grade_level': "SEC"
        }, request=request)
        self.assertFalse(form.is_valid())
        # 可选:验证错误信息
        self.assertIn("A course with that name already exists.", form.errors['__all__'])

2. 优化表单的__init__方法(可选)

为避免因忘记传request导致KeyError,可给request设置默认值并添加参数检查:

def __init__(self, *args, **kwargs):
    self.request = kwargs.pop('request', None)
    if not self.request:
        raise ValueError("request参数必须传入")
    self.qc = Course.objects.filter(user=self.request.user)
    super().__init__(*args, **kwargs)

原因说明

  • 业务视图中调用表单时,通过CourseForm(request.POST, request=request)传入request关键字参数,符合表单__init__的要求。
  • 测试用例错误地将self.user作为位置参数传入,表单的__init__会从kwargs中取request,导致找不到该参数抛出KeyError。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 10:45:38