Reputation: 13208
I have a Django ModelForm where I want one field to be treated as conditionally required. I want it to be required by default, but not required depending on the value of another field. My model looks something along the lines of:
class MyModel(models.Model):
foo = models.BooleanField()
bar = models.CharField(choices=(('A', 'A'),('B', 'B'),('C', 'C')),)
Neither have null=True
or blank=True
so both appear as "required" when I render my form, which is what I want.
However, I have some javascript on my form template that hides the bar
field input depending on the value of the input for foo
. In this case, when the form is submitted, I don't want to have any validation errors about bar
being required.
I am trying to implement this logic in my ModelForm's clean
method, like so:
def clean(self):
data = super(MyModelForm, self).clean()
if data.get('foo') == True and 'bar' in self.errors:
del self.errors['bar']
return data
However, this still seems to give me a "This field cannot be blank." error. I've noticed that my ModelForm also has an _errors
dict, but if I try adding del self._errors['bar']
I end up with an exception.
I have found some ways to do the opposite of what I want(1, 2), which is have the field not be required by default and then check it should be required in some cases in clean
, but this is not what I want.
I've also tried adding data['bar'] = " "
as a workaround but then I get an error about not choosing one of the choices. Ideally, I'd rather just be able to save the empty string.
Upvotes: 1
Views: 3888
Reputation: 957
You can override the _clean_fields
method in order to provide your conditional changes:
def _clean_fields(self):
if self.data.get('foo', None) == True:
self.fields['bar'].required = False
super(MyForm, self)._clean_fields()
Upvotes: 0
Reputation: 13208
The issue here is that in addition to the form's clean
method being called, the form also calls post_clean
on the form's instance. Because the field on the model does not have blank=True
, when the model is cleaned another error is raised and it gets passed back to the form.
To resolve, set blank=True
on the model. This has the unwanted side effect of making the field appear as not required on the form. To make the field required on the form, simply set the required flag in init:
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__( *args, **kwargs)
self.fields['bar'].required = True
This, in addition to the clean
method in the original question, is enough to create the conditionally required field that is required by default and not-required based on the value of another field.
Upvotes: 1