I'm using Django 1.4 with Python 2.7 on Ubuntu 12.10.
I have a form where I need to populate a few drop-downs dynamically (using jQuery) but need 2 of them to be required and the 3rd to be optional.
I'm using Tastypie to help with the API to get the options. Basically the first drop-down is populated with industry level codes for schools. Once a code is selected a category drop-down is populated for all categories for that code. Once the category is chosen a subcategory drop-down is populated for all subcategories for that combination of code and category.
I'm able to require the code drop-down (it's not dynamically populated). However, I'm having a tough time getting the category drop-down to be required. There are basically 2 routes I can take - front-end validation or back-end validation. I'm trying to go with back-end validation so I can easily create further validation if needed.
Here is the form:
class SchoolProductForm(forms.ModelForm):
cip_category = forms.ChoiceField(required=True,
choices=(('', '----------'),))
def __init__(self, *args, **kwargs):
super(SchoolProductForm, self).__init__(*args, **kwargs)
self.fields['short_description'].widget = TA_WIDGET
self.fields['salary_info'].widget = TA_WIDGET
self.fields['job_opportunities'].widget = TA_WIDGET
self.fields['related_careers'].widget = TA_WIDGET
self.fields['meta_keywords'].widget = TI_WIDGET
self.fields['meta_description'].widget = TI_WIDGET
self.fields['cip'].queryset = models.CIP.objects.filter(
parent_id__isnull=True)
class Meta:
model = models.SchoolProduct
exclude = ('campus',)
I've tried to override the clean
method. I've tried to create a field specific clean
method. Neither seem to work.
Variations of the following:
def clean(self):
super(SchoolProductForm, self).clean()
if cip_category in self._errors:
del self._errors['cip_category']
if self.cleaned_data['cip_category'] == '----------':
self._errors['cip_category'] = 'This field is required.'
return self.cleaned_data
This gives an error that there is no cip_category
in cleaned_data
, which makes sense because it didn't validate.
I've tried variations with the field specific clean:
def clean_cip_category(self):
data = self.cleaned_data['cip_category']
self.fields['cip_category'].choices = data
return data
But get a validation error on the page stating my choice is not one of the available choices.
I've tried to create a dynamic field type (several variations):
class DynamicChoiceField(forms.ChoiceField):
def valid_value(self, value):
return True
class SchoolProductForm(forms.ModelForm):
cip_category = DynamicChoiceField(required=True,
choices=(('', '----------'),))
But it accepts ----------
as a valid option (which I don't want) and causes an error since the ORM tries to match a value of ----------
in the database (which it won't find).
Any ideas?
I was able to solve this with a little overriding of a method in ChoiceField
.
I added the field to the form and handled the pre-population with the self.initial
:
class SchoolProductForm(forms.ModelForm):
cip_category = common_forms.DynamicChoiceField(
required=True, choices=(('', '----------'),))
def __init__(self, *args, **kwargs):
super(SchoolProductForm, self).__init__(*args, **kwargs)
self.fields['short_description'].widget = TA_WIDGET
self.fields['salary_info'].widget = TA_WIDGET
self.fields['job_opportunities'].widget = TA_WIDGET
self.fields['related_careers'].widget = TA_WIDGET
self.fields['meta_keywords'].widget = TI_WIDGET
self.fields['meta_description'].widget = TI_WIDGET
self.fields['cip'].queryset = models.CIP.objects.filter(
parent_id__isnull=True)
# Get the top parent and pre-populate
if 'cip' in self.initial:
self.initial['cip'] = models.CIP.objects.get(
pk=self.initial['cip']).top_parent()
class Meta:
model = models.SchoolProduct
exclude = ('campus',)
Where DynamicChoiceField
is:
class DynamicChoiceField(forms.ChoiceField):
def valid_value(self, value):
return True
Then, in the view I added handling in the form_valid
override:
def form_valid(self, form):
self.object = form.save(commit=False)
# Handle the CIP code
self.object.cip_id = self.request.POST.get('cip_subcategory')
if self.object.cip_id == '':
self.object.cip_id = self.request.POST.get('cip_category')
self.object.save()