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 Orange
s that aren't already assigned to other Apple
s. 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?
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.