Skip to content

Instantly share code, notes, and snippets.

@dir01
Created August 17, 2012 10:03
Show Gist options
  • Save dir01/3377660 to your computer and use it in GitHub Desktop.
Save dir01/3377660 to your computer and use it in GitHub Desktop.
Django dynamic forms
import unittest2
from django import forms
class DynamicForm(forms.Form):
"""
Allows to alter fields amount and requireness via spec, e.g.:
form = DynamicFormSubclass(spec={
'last_name': False, # Drops field from initial form
'gender': {'required': True},
'date_of_birth': {'required': False},
})
"""
def __init__(self, *args, **kwargs):
spec = kwargs.pop('spec')
super(DynamicForm, self).__init__(*args, **kwargs)
self.alter_form_by_spec(spec)
def alter_form_by_spec(self, spec):
for name, field in self.fields.items():
field_spec = spec.get(name, None)
if not field_spec:
self.fields.pop(name)
else:
self.alter_field_by_spec(field, field_spec)
def alter_field_by_spec(self, field, field_spec):
field.required = field_spec.get('required', True)
class DynamicFormSpecForm(forms.Form):
EXCLUDE, OPTIONAL, REQUIRED = 0, 1, 2
CHOICES = [(EXCLUDE, "Exclude"), (OPTIONAL, "Optional"), (REQUIRED, "Required")]
FORM_VALUE_TO_SPEC_VALUE_MAP = {
EXCLUDE: None,
OPTIONAL: {'required': False},
REQUIRED: {'required': True},
}
dynamic_form_class = NotImplemented
def __init__(self, *args, **kwargs):
for name, field in self.dynamic_form_class.base_fields.items():
self.base_fields[name] = self.get_spec_field_for_field(field)
super(DynamicFormSpecForm, self).__init__(*args, **kwargs)
def get_spec(self):
self.full_clean()
spec = dict()
for key, value in self.cleaned_data.iteritems():
spec[key] = self.FORM_VALUE_TO_SPEC_VALUE_MAP[int(value)]
return spec
def get_spec_field_for_field(self, field):
initial = self.REQUIRED if field.required else self.OPTIONAL
return forms.ChoiceField(choices=self.CHOICES, widget=forms.RadioSelect,
initial=initial, label=field.label,
)
if __name__ == '__main__':
class DynamicFormUnderTest(DynamicForm):
field1 = forms.CharField(label='One', required=False, widget=forms.Textarea)
field2 = forms.CharField(label='Two')
class TestDynamicForm(unittest2.TestCase):
def runTest(self):
spec = {
'field1': {'required': True},
'field2': False,
}
form = DynamicFormUnderTest(spec=spec)
self.assertIn('field1', form.fields)
self.assertTrue(form.fields['field1'].required)
self.assertNotIn('field2', form.fields)
class DynamicFormSpecFormUnderTest(DynamicFormSpecForm):
dynamic_form_class = DynamicFormUnderTest
class TestDynamicFormSpecForm(unittest2.TestCase):
def test_fields_in_form(self):
fields = self.get_form_fields()
self.assertIn('field1', fields)
self.assertIn('field2', fields)
def test_fields_in_spec_form_copy_verbose_names_from_dynamic_form(self):
field1, field2 = self.get_field1_field2_tuple()
self.assertEqual('One', field1.label)
self.assertEqual('Two', field2.label)
def test_fields_in_spec_form_has_initial_values_from_dynamic_form(self):
field1, field2 = self.get_field1_field2_tuple()
self.assertEqual(DynamicFormSpecForm.OPTIONAL, field1.initial)
self.assertEqual(DynamicFormSpecForm.REQUIRED, field2.initial)
def test_spec_form_fields_are_required(self):
field1, field2 = self.get_field1_field2_tuple()
self.assertTrue(field1.required)
self.assertTrue(field2.required)
def test_spec_generation(self):
data = {
'field1': DynamicFormSpecForm.EXCLUDE,
'field2': DynamicFormSpecForm.REQUIRED
}
form = self.get_form(data=data)
expected_spec = {'field1': None, 'field2': {'required': True}}
self.assertDictEqual(expected_spec, form.get_spec())
def get_field1_field2_tuple(self):
return self.get_fields_tuple(['field1', 'field2'])
def get_fields_tuple(self, names):
fields = self.get_form_fields()
return [fields[field_name] for field_name in names]
def get_form_fields(self):
return self.get_form().fields
def get_form(self, data=None):
form = DynamicFormSpecFormUnderTest(data=data)
return form
unittest2.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment