带有 ModelChoiceField 的 Django ModelForm 的测试用例
Test case for Django ModelForm with ModelChoiceField
我正在尝试编写一个简单的测试用例来测试允许将 Orange
对象分配给 Apple
对象的 Django 表单。
forms.py
class AppleOrangeAssignmentForm(forms.ModelForm):
orange = forms.ModelChoiceField(required=True, \
queryset=Orange.objects.filter(apple=None))
class Meta:
model = Apple
fields = ('orange')
orange
上的 queryset
用于确保下拉列表中的值只是 Orange
尚未分配给其他 Apple
的值。此代码在调用它的视图中正确且一致地工作。
在下面的测试用例中,我创建了一个全新的 Orange
以确保我有一个未分配给其他任何地方的。
test.py
def test_apple_orange_assignment(self):
apple = Apple.objects.get(pk=1)
self.assertEquals(apple.orange, None)
orange = Orange.objects.create(name='clementime')
form_data = { 'orange': orange }
form = AppleOrangeAssignmentForm(data=form_data, instance=apple)
self.assertTrue(form.is_valid()) # <=== Fails here
奇怪的是,表单在测试用例中没有通过验证! form.errors
说:{'orange': ['Select a valid choice. That choice is not one of the available choices.']}
。当我进一步挖掘时,我可以看到我尝试分配的 orange
确实出现在 form.fields['orange'].queryset
中。
我已经尽了一切努力来验证这一点。我尝试将表单字段中的 queryset
更改为 Orange.objects.all()
。我尝试将测试用例中 orange
变量的创建更改为 form.fields['orange'].queryset[0]
以确保我选择的是其选择中的橙色。但是没有任何效果。
正如我所说,这一切在视图中都完美无缺。任何想法我在测试用例中做错了什么?谢谢!
首先不需要在required=True \
后面加上\
,因为语句会以括号结尾。
在实例化表单时需要指定对象id而不是整个橙色对象class。
def test_apple_orange_assignment(self):
# More code here
form_data = { 'orange': orange.id }
form = AppleOrangeAssignmentForm(data=form_data, instance=apple)
assert form.is_valid(), form.errors
就是这样!
提示:您可以使用assert False, form.as_p()
使测试失败并跟踪表单html,您会发现它不是在寻找对象而是object_id.
来自 Django 关于 Django 在表单中的作用的官方文档:
receiving and processing submitted forms and data from the client
表单需要来自客户端的数据,而不是以编程方式。在您的测试中,您尝试传递一个 python class (Orange
) 的实例 (orange
),但是由于 django 表单是为接受来自客户端的数据而构建的,它允许对象没有意义,而只允许客户端可以输入的值,例如整数、字符串、浮点数等
因此,Django 将 ForeignKey 字段转换为 ModelChoiceField 并期望输入是实例的 id 而不是实例本身,因此当您传递实例时,表单会尝试将传递的实例作为 id 进行验证,因此失败。
来自docs
class ModelChoiceField(**kwargs)
Default widget: Select
Empty value: None
Normalizes to: A model instance.
Validates that the given id exists in the queryset.
Error message keys: required, invalid_choice
示例解决方案:
form_data = {'orange': orange.id}
我正在尝试编写一个简单的测试用例来测试允许将 Orange
对象分配给 Apple
对象的 Django 表单。
forms.py
class AppleOrangeAssignmentForm(forms.ModelForm):
orange = forms.ModelChoiceField(required=True, \
queryset=Orange.objects.filter(apple=None))
class Meta:
model = Apple
fields = ('orange')
orange
上的 queryset
用于确保下拉列表中的值只是 Orange
尚未分配给其他 Apple
的值。此代码在调用它的视图中正确且一致地工作。
在下面的测试用例中,我创建了一个全新的 Orange
以确保我有一个未分配给其他任何地方的。
test.py
def test_apple_orange_assignment(self):
apple = Apple.objects.get(pk=1)
self.assertEquals(apple.orange, None)
orange = Orange.objects.create(name='clementime')
form_data = { 'orange': orange }
form = AppleOrangeAssignmentForm(data=form_data, instance=apple)
self.assertTrue(form.is_valid()) # <=== Fails here
奇怪的是,表单在测试用例中没有通过验证! form.errors
说:{'orange': ['Select a valid choice. That choice is not one of the available choices.']}
。当我进一步挖掘时,我可以看到我尝试分配的 orange
确实出现在 form.fields['orange'].queryset
中。
我已经尽了一切努力来验证这一点。我尝试将表单字段中的 queryset
更改为 Orange.objects.all()
。我尝试将测试用例中 orange
变量的创建更改为 form.fields['orange'].queryset[0]
以确保我选择的是其选择中的橙色。但是没有任何效果。
正如我所说,这一切在视图中都完美无缺。任何想法我在测试用例中做错了什么?谢谢!
首先不需要在required=True \
后面加上\
,因为语句会以括号结尾。
在实例化表单时需要指定对象id而不是整个橙色对象class。
def test_apple_orange_assignment(self):
# More code here
form_data = { 'orange': orange.id }
form = AppleOrangeAssignmentForm(data=form_data, instance=apple)
assert form.is_valid(), form.errors
就是这样!
提示:您可以使用assert False, form.as_p()
使测试失败并跟踪表单html,您会发现它不是在寻找对象而是object_id.
来自 Django 关于 Django 在表单中的作用的官方文档:
receiving and processing submitted forms and data from the client
表单需要来自客户端的数据,而不是以编程方式。在您的测试中,您尝试传递一个 python class (Orange
) 的实例 (orange
),但是由于 django 表单是为接受来自客户端的数据而构建的,它允许对象没有意义,而只允许客户端可以输入的值,例如整数、字符串、浮点数等
因此,Django 将 ForeignKey 字段转换为 ModelChoiceField 并期望输入是实例的 id 而不是实例本身,因此当您传递实例时,表单会尝试将传递的实例作为 id 进行验证,因此失败。
来自docs
class ModelChoiceField(**kwargs)
Default widget: Select
Empty value: None
Normalizes to: A model instance.
Validates that the given id exists in the queryset.
Error message keys: required, invalid_choice
示例解决方案:
form_data = {'orange': orange.id}