-
Notifications
You must be signed in to change notification settings - Fork 718
/
forms.py
153 lines (113 loc) · 4.52 KB
/
forms.py
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
from dominate import tags
from dominate.util import raw
from flask import current_app
from markupsafe import Markup
from visitor import Visitor
def render_form(form, **kwargs):
r = WTFormsRenderer(**kwargs)
return Markup(r.visit(form))
class WTFormsRenderer(Visitor):
def __init__(self,
action='',
id=None,
method='post',
extra_classes=[],
role='form',
enctype=None):
self.action = action
self.form_id = id
self.method = method
self.extra_classes = extra_classes
self.role = role
self.enctype = enctype
def _visited_file_field(self):
if self._real_enctype is None:
self._real_enctype = u'multipart/form-data'
def _get_wrap(self, node, classes='form-group'):
# add required class, which strictly speaking isn't bootstrap, but
# a common enough customization
if node.flags.required:
classes += ' required'
div = tags.div(_class=classes)
if current_app.debug:
div.add(tags.comment(' Field: {} ({}) '.format(
node.name, node.__class__.__name__)))
return div
def _wrapped_input(self, node,
type='text',
classes=['form-control'], **kwargs):
wrap = self._get_wrap(node)
wrap.add(tags.label(node.label.text, _for=node.id))
wrap.add(tags.input(type=type, _class=' '.join(classes), **kwargs))
return wrap
def visit_BooleanField(self, node):
wrap = self._get_wrap(node, classes='checkbox')
label = wrap.add(tags.label(_for=node.id))
label.add(tags.input(type='checkbox'))
label.add(node.label.text)
return wrap
def visit_DateField(self, node):
return self._wrapped_input(node, 'date')
def visit_DateTimeField(self, node):
return self._wrapped_input(node, 'datetime-local')
def visit_DecimalField(self, node):
# FIXME: if range-validator is present, add limits?
return self._wrapped_input(node, 'number')
def visit_EmailField(self, node):
# note: WTForms does not actually have an EmailField, this function
# is called by visit_StringField based on which validators are enabled
return self._wrapped_input(node, 'email')
def visit_Field(self, node):
# FIXME: add error class
wrap = self._get_wrap(node)
# add the label
wrap.add(tags.label(node.label.text, _for=node.id))
wrap.add(raw(node()))
if node.description:
wrap.add(tags.p(node.description, _class='help-block'))
return wrap
def visit_FileField(self, node):
self._visited_file_field()
return self._wrapped_input(node, 'file', classes=[])
def visit_FloatField(self, node):
# FIXME: if range-validator is present, add limits?
return self._wrapped_input(node, 'number')
def visit_Form(self, node):
form = tags.form(_class=' '.join(['form'] + self.extra_classes))
if self.action:
form['action'] = self.action
if self.form_id:
form['id'] = self.form_id
if self.method:
form['method'] = self.method
# prepare enctype, this will be auto-updated by file fields if
# necessary
self._real_enctype = self.enctype
# render fields
for field in node:
elem = self.visit(field)
form.add(elem)
if self._real_enctype:
form['enctype'] = self._real_enctype
return form
def visit_HiddenField(self, node):
return raw(node())
def visit_IntegerField(self, node):
# FIXME: if range-validator is present, add limits?
return self._wrapped_input(node, 'number', step=1)
def visit_PasswordField(self, node):
return self._wrapped_input(node, 'password')
def visit_SubmitField(self, node):
button = tags.button(node.label.text,
_class='btn btn-default',
type='submit')
return button
def visit_TextField(self, node):
# legacy support for TextField, deprecated in WTForms 2.0
return self.visit_StringField(node)
def visit_StringField(self, node):
for v in node.validators:
if v.__class__.__name__ == 'Email':
# render email fields differently
return self.visit_EmailField(node)
return self._wrapped_input(node, 'text')