我正在尝试编写一个简单的测试用例来测试Django表单,该表单允许将Orange
对象分配给Apple
对象。
forms.py
class AppleOrangeAssignmentForm(forms.ModelForm):
orange = forms.ModelChoiceField(required=True, \
queryset=Orange.objects.filter(apple=None))
class Meta:
model = Apple
fields = ('orange')
queryset
上的orange
用于确保下拉列表中的值仅为Orange
个尚未分配给其他Apple
的值。此代码在调用它的视图中正确且一致地工作。
在下面的测试用例中,我创建了一个全新的Orange
,以确保我有一个未在其他地方分配的。{/ p>
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]
,以确保我选择了一个橙色。但没有任何作用。
正如我所说,这一切在视图中完美运作。在测试用例中我有什么想法吗?谢谢!
答案 0 :(得分:3)
首先,不需要在\
之后放置required=True \
,因为该语句将以括号结束。
在实例化表单类时,需要指定对象id而不是整个橙色对象。
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。
答案 1 :(得分:1)
来自Django关于Django在形式中角色主题的官方文档:
接收和处理来自客户的提交表格和数据
表单期望来自客户端的数据不是以编程方式提供的。在您的测试中,您尝试传递python类(orange
)的实例(Orange
),但由于django表单是为了接受来自客户端的数据而构建的,因此允许对象没有意义,而只是可以由客户输入的值,例如整数,字符串,浮点数等
因此,Django将ForeignKey字段转换为ModelChoiceField,并期望输入是实例的id而不是实例本身,因此当您传递实例时,表单会尝试使用传递的实例作为id进行验证,从而失败。
来自docs
class ModelChoiceField(** kwargs)
默认小部件:选择
空值:无
规范化为:模型实例。
验证查询集中是否存在给定的ID。
错误消息键:必需,invalid_choice
示例解决方案:
form_data = {'orange': orange.id}