Formset Form

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
"""
This class when mixed into a form class provides hooks for template designers to 
access individual fields or sets of fields based on various criteria such as field 
name prefix or fields appearing before or after certain fields in the form, while 
still being able to use the as_* form rendering methods against these fields. 

Credits:
--------

Stephen McDonald <steve@jupo.org>

License:
--------

Creative Commons Attribution-Share Alike 3.0 License
http://creativecommons.org/licenses/by-sa/3.0/

When attributing this work, you must maintain the Credits
paragraph above.
"""

from copy import copy
from itertools import dropwhile, takewhile
from re import match
 
from django.utils.datastructures import SortedDict
 

class FormsetForm(object):
    """
    Form mixin that gives template designers greater control over form field 
    rendering while still using the as_* methods. The following methods return 
    a copy of the form with only a subset of fields that can then be rendered
    with as_* methods.
    
    {{ form.PREFIX_fields.as_* }} - fields with a name starting with PREFIX
    {{ form.FIELD_field.as_* }} - a single field with the name FIELD
    {{ form.fields_before_FIELD.as_* }} - fields before the field named FIELD
    {{ form.fields_after_FIELD.as_* }} - fields after the field named FIELD
    {{ form.other_fields.as_* }} - fields not yet accessed as a formset
 
    CSS classes are also added to the rendered fields. These are the lowercase 
    classname of the widget, eg "textinput" or "checkboxinput", and if the field 
    is required the class of "required" is also added.
    """
 
    def _fieldset(self, field_names):
        """
        Return a subset of fields by making a copy of the form containing only 
        the given field names and adding extra CSS classes to the fields.
        """
        fieldset = copy(self)
        if not hasattr(self, "_fields_done"):
            self._fields_done = []
        else:
            # all fieldsets will contain all non-field errors, so for fieldsets
            # other than the first ensure the call to non-field errors does nothing
            fieldset.non_field_errors = lambda *args: None
        field_names = filter(lambda f: f not in self._fields_done, field_names)
        fieldset.fields = SortedDict([(f, self.fields[f]) for f in field_names])
        for field in fieldset.fields.values():
            field.widget.attrs["class"] = field.widget.__class__.__name__.lower()
            if field.required:
                field.widget.attrs["class"] += " required"
        self._fields_done.extend(field_names)
        return fieldset
 
    def __getattr__(self, name):
        """
        Dynamic fieldset caller - matches requested attribute name against 
        pattern for creating the list of field names to use for the fieldset. 
        """
        filters = (
            ("^other_fields$", lambda: 
                self.fields.keys()),
            ("^(\w*)_fields$", lambda name: 
                [f for f in self.fields.keys() if f.startswith(name)]),
            ("^(\w*)_field$", lambda name: 
                [f for f in self.fields.keys() if f == name]),
            ("^fields_before_(\w*)$", lambda name: 
                takewhile(lambda f: f != name, self.fields.keys())),
            ("^fields_after_(\w*)$", lambda name: 
                list(dropwhile(lambda f: f != name, self.fields.keys()))[1:]),
        )
        for filter_exp, filter_func in filters:
            filter_args = match(filter_exp, name)
            if filter_args is not None:
                return self._fieldset(filter_func(*filter_args.groups()))
        raise AttributeError(name)

More like this

  1. FieldAccessForm (per-field user access for forms derived from models) by Killarny 5 years, 6 months ago
  2. Custom CSS class in Form with template tag filter by kegan 4 years, 10 months ago
  3. TemplatedForm by alexkoval 7 years, 1 month ago
  4. BetterForm with fieldsets and row_attrs by carljm 5 years, 2 months ago
  5. Arbitrary length formset by Rupe 4 years, 7 months ago

Comments

(Forgotten your password?)