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
相关产品推荐
相关产品推荐

