使用ModelChoiceField

时间:2018-04-03 04:19:44

标签: django django-testing modelform

我正在尝试编写一个简单的测试用例来测试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],以确保我选择了一个橙色。但没有任何作用。

正如我所说,这一切在视图中完美运作。在测试用例中我有什么想法吗?谢谢!

2 个答案:

答案 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}