Ticket #6630: metaforms.py

File metaforms.py, 2.2 KB (added by mitar, 5 years ago)

A simple approach to fieldsets

1from django.contrib.admin import util as admin_util
3class FieldsetBoundField(forms.BoundField):
4        """
5        This class extends `django.forms.forms import.BoundField` to also carry information about the fieldset this field is in.
6        """
8        def __init__(self, form, field, name, fieldset):
9                super(FieldsetBoundField, self).__init__(form, field, name)
10                self.fieldset = fieldset
12class FieldsetFormMixin(object):
13        """
14        This mixin class defines methods to use with other form classes to extend them to return `FieldsetBoundField` when accessing
15        forms' fields. If such form class has `fieldset` attribute defined it is used to attach fieldset to all fields, which
16        are returned as `FieldsetBoundField`.
18        `fieldset` attribute should have the same structure as that for `django.contrib.admin.ModelAdmin`. The attached fieldset is
19        the given fieldset dictionary with `name` set to the name of the fieldset.
21        It should be listed as the parent class before `django.forms.models.ModelForm` based classes so that methods here take
22        precedence.
23        """
25        def __iter__(self):
26                """
27                If `fieldset` attribute is not defined we iterate normally. Otherwise we iterate in the order in which fields
28                are defined in `fieldset` attribute. In the later case we return fields as `FieldsetBoundField`.
29                """
31                if not hasattr(self, 'fieldset'):
32                        for field in super(FieldsetFormMixin, self).__iter__():
33                                yield field
34                else:
35                        for field in admin_util.flatten_fieldsets(self.fieldset):
36                                yield self[field]
38        def __getitem__(self, name):
39                """
40                If `fieldset` attribute is not defined we return the field normally. Otherwise we return the field as `FieldsetBoundField`
41                with fieldset attached to it. It the later case the field has to be defined in `fieldset` attribute.
42                """
44                field = super(FieldsetFormMixin, self).__getitem__(name)
45                if not hasattr(self, 'fieldset'):
46                        return field
47                else:
48                        fieldset = None
49                        for fname, fset in self.fieldset:
50                                if name in fset['fields']:
51                                        # We copy dictionary here so that we do not dirty it with later changes
52                                        fieldset = fset.copy()
53                                        fieldset['name'] = fname
54                                        break
55                        if not fieldset:
56                                raise KeyError('Key %r not defined in any fieldset in Form' % name)
57                        return FieldsetBoundField(self, field.field, field.name, fieldset)
Back to Top