djangodjango-testingmodelform

Test case for Django ModelForm with ModelChoiceField


I'm trying to write a simple test case to test a Django form that permits the assignment of an Orange object to an Apple object.

forms.py

class AppleOrangeAssignmentForm(forms.ModelForm):
    orange = forms.ModelChoiceField(required=True, \ 
                 queryset=Orange.objects.filter(apple=None))

class Meta:
    model = Apple
    fields = ('orange')

The queryset on orange is there to ensure that the values in the dropdown are only Oranges that aren't already assigned to other Apples. This code works correctly and consistently in the view that calls it.

In the test case below, I am create a brand new Orange to ensure that I have one that it is not assigned anywhere else.

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

Weirdly, the form does not validate in the test case! form.errors says: {'orange': ['Select a valid choice. That choice is not one of the available choices.']}. When I dig in further, I can see that the orange I am trying to assign does appear in form.fields['orange'].queryset, though.

I have tried everything to try to get this to validate. I have tried changing the queryset in the form field to Orange.objects.all(). I have tried changing the creation of the orange variable in the test case to form.fields['orange'].queryset[0] to ensure I'm picking an orange that is in its choices. But nothing works.

As I said, this all works perfectly in the view. What am I doing wrong here in the test case?


Solution

  • Firstly there's no need to put a \ after required=True \ because the statement will end with a bracket.

    You need to specify the object id rather than the whole orange object when instantiating the form 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
    

    That's it!

    Tip: You can use assert False, form.as_p() to make the test fail and trace the form html, and there you'll find that it isn't looking for the object but the object_id.