Created
August 17, 2012 10:03
-
-
Save dir01/3377660 to your computer and use it in GitHub Desktop.
Django dynamic forms
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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