FieldsetForm

 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
90
91
92
93
94
95
96
97
98
from django.newforms.forms import BaseForm, BoundField, ErrorList
from django.utils.html import escape
    
def include_formfield_callback(*alloweds):
    def newfunc(f, **kwargs):
        return [None, f.formfield(**kwargs)][f.name in alloweds]	
    newfunc.alloweds = alloweds
    return newfunc

def form_fieldsets(*fieldsets):
    allowed_fields = []
    for fieldset in fieldsets:
        allowed_fields += fieldset[1]['fields']
        
    # TODO: Study whether we need to add separate name for each type we produce dynamically
    return {
        'form': type("DynamicFieldsetForm", (FieldsetForm,), {"fieldsets":fieldsets}), 
        'formfield_callback' : include_formfield_callback(*allowed_fields) }

class FieldsetForm(BaseForm):
    def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row, normal_fieldset, fieldset_nameholder):
        top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
        output = []
        
        for fieldset in self.fieldsets:
            fieldset_name = fieldset[0]
            fieldset_content = fieldset[1]
            fieldset_field_names = []
            fieldset_classes = ""
            normal_fieldset_rows = []
            # TODO: Test hidden fields
            hidden_fields = []
            
            # TODO: Implement error check (fields supposed to be list, and classes supposed to be string)
            if fieldset_content.has_key('fields'):
                fieldset_field_names = fieldset_content['fields']
            if fieldset_content.has_key('classes'):
                fieldset_classes = fieldset_content['classes']
            
            normal_fieldset_nameholder = ""
            if fieldset_name:
                normal_fieldset_nameholder = fieldset_nameholder % {'fieldset_name':fieldset_name}
            
            # Contents of this for loop is almost identical with BaseForm loop (except that I use normal_fieldset_rows at the end)
            for field_name in fieldset_field_names:
                name = field_name
                field = self.fields[field_name]
                
                bf = BoundField(self, field, name)
                bf_errors = ErrorList([escape(error) for error in bf.errors]) # Escape and cache in local variable.
                if bf.is_hidden:
                    if bf_errors:
                        top_errors.extend(['(Hidden field %s) %s' % (name, e) for e in bf_errors])
                    hidden_fields.append(unicode(bf))
                else:
                    if errors_on_separate_row and bf_errors:
                        output.append(error_row % bf_errors)
                    label = bf.label and bf.label_tag(escape(bf.label + ':')) or ''
                    if field.help_text:
                        help_text = help_text_html % field.help_text
                    else:
                        help_text = u''
                    normal_fieldset_rows.append(normal_row % {'errors': bf_errors, 'label': label, 'field': unicode(bf), 'help_text': help_text})
                        
            output.append(normal_fieldset % {
                'fieldset_nameholder':      normal_fieldset_nameholder, 
                'fieldset_rows' :           "\n".join(normal_fieldset_rows), 
                'fieldset_hidden_fields':   "\n".join(hidden_fields), 
                'fieldset_classes' :        fieldset_classes})

        if top_errors:
            output.insert(0, error_row % top_errors)

        return u'\n'.join(output)

    def as_table(self):
        "Returns this form rendered as HTML <table> -- including the <table></table>."
        # TODO: Is class attribute allowed to be empty?
        # TODO: Add ability to add extra attributes to table element and fieldset element
        # TODO: Think about moving all this to templates (since it sounds more sane)
        #   It is not done that way yet because this is just first scetch (and BaseForm did this way)
        
        return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False, u'<fieldset class="%(fieldset_classes)s">%(fieldset_nameholder)s<table class="form">%(fieldset_rows)s</table>%(fieldset_hidden_fields)s</fieldset>', u"<legend>%(fieldset_name)s</legend>")

    # I'm too excited to define the following, so we shall just pass at this point:
    def as_ul(self):
        pass
        '''
        "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
        return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
        '''

    def as_p(self):
        pass
        '''
        "Returns this form rendered as HTML <p>s."
        return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'<p>%s</p>', '</p>', u' %s', True)
        '''

More like this

  1. Database file storage by powerfox 5 years, 2 months ago
  2. ManyToManyField no syncdb by powerfox 5 years, 3 months ago
  3. Easy Form Structuring by jug 4 years, 9 months ago
  4. View Permission Decorator Helper by jgeewax 5 years, 9 months ago
  5. Custom model field for Frame or Box by binishkaspar 3 years, 2 months ago

Comments

gderazon (on November 14, 2008):

To make this work with django 1.0 wrap line 74 with mark_safe: mark_safe(u'\n'.join(output))

In addition it was not clear to me how to use it in my form, so here is an example:

class MyForm(FieldsetForm):

def __init__(self, *args, **kwargs):

    super(FieldsetForm, self).__init__(*args, **kwargs)

    self.fieldsets = ((None,{'fields': ('name1','email1')} ),
            ("Advanced options", {'fields': ('name1','email1'), 'classes':'collapse'})
        )

name1 = forms.CharField(label="Name (1)")
email1 = forms.EmailField(label="E-mail address (1)")

name2 = forms.CharField(label="Name (2)")
email2 = forms.EmailField(label="E-mail address (2)")

#

gderazon (on November 18, 2008):

More changes in FieldsetForm to make it work

class FieldsetForm(BaseForm): metaclass = DeclarativeFieldsMetaclass

def __init__(self, *args, **kwargs):
    super(FieldsetForm, self).__init__(*args, **kwargs)
    self.fieldsets = None

#

Ciantic (on February 16, 2009):

Heh, I can't believe someone used this. I rediscovered this using Google.

I discarded this because it should be inbuilt to Django, and I don't mind upgrading these helpers too much.

#

akaihola (on October 6, 2011):

@Ciantic, umm, I wouldn't call my fork of WTForm maintained...

#

(Forgotten your password?)