带有 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}