/
djblets_forms.py
211 lines (154 loc) · 5.54 KB
/
djblets_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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
"""Form-related template tags."""
from django import forms, template
from django.utils.html import conditional_escape, format_html
register = template.Library()
@register.simple_tag
def label_tag(field):
"""Render the tag for a field's label.
This generates labels similar to the administration UI's own field labels,
providing styling for required fields and checkboxes.
If the label is explicitly set to an empty string on the field, no label
will be rendered, matching Django's default behavior.
Args:
field (django.forms.BoundField):
The bound field on the form to render the label for.
Returns:
django.utils.safestring.SafeText:
The resulting HTML for the label.
Example:
.. code-block:: html+django
{% label_for form.my_field %}
"""
if not field.label:
return ''
is_checkbox = is_field_checkbox(field)
# Build the list of CSS classes to apply to the label.
classes = []
if field.field.required:
classes.append('required')
if is_checkbox:
classes.append('vCheckboxLabel')
if classes:
classes_html = format_html(' class="{0}"', ' '.join(classes))
else:
classes_html = ''
# Build the text for the label.
label = field.label
if not is_checkbox:
label += ':'
# Render the result.
return format_html('<label for="{0}"{1}>{2}</label>',
form_field_id(field),
classes_html,
label)
@register.filter
def form_field_id(field):
"""Render the ID of a field.
This will derive the field's ID in the form and output it, for use in
utility functions or custom HTML.
Args:
field (django.forms.BoundField):
The bound field on the form.
Returns:
django.utils.safestring.SafeText:
The resulting ID as safe HTML.
Example:
.. code-block:: html+django
<span data-field-id="{{form.my_field|form_field_id}}"></span>
"""
widget = field.field.widget
field_id = widget.attrs.get('id') or field.auto_id or ''
if field_id:
field_id = widget.id_for_label(field_id)
return conditional_escape(field_id)
@register.filter
def is_field_checkbox(field):
"""Return whether or not this field is effectively a checkbox field.
A field is considered to be a checkbox field if its widget is a
:py:class:`~django.forms.CheckboxInput`.
Args:
field (django.forms.BoundField):
The bound field on the form.
Returns:
bool:
``True`` if this is a checkbox field. ``False`` if not.
Example:
.. code-block:: html+django
{% if field|is_field_checkbox %}
...
{% endif %}
"""
return isinstance(field.field.widget, forms.CheckboxInput)
@register.filter
def is_checkbox_row(field):
"""Return whether the field's row is a checkbox-ish row.
This will return ``True`` if rendering a checkbox, radio button, or
multi-select checkbox.
Args:
field (django.forms.BoundField):
The bound field on the form.
Returns:
bool:
``True`` if this is a checkbox-ish field. ``False`` if not.
Example:
.. code-block:: html+django
{% if field|is_checkbox_row %}
...
{% endif %}
"""
return isinstance(field.field.widget, (forms.CheckboxInput,
forms.RadioSelect,
forms.CheckboxSelectMultiple))
@register.filter
def form_field_has_label_first(field):
"""Return whether a form label should be displayed before the widget.
This helps when rendering labels and widgets in the correct order.
Typically, non-checkbox widgets are preceded by a label, and this lets
templates determine if that should be the case for a given field.
Args:
field (django.forms.BoundField):
The bound field on the form.
Returns:
bool:
``True`` if the label should appear before the widget.
``False`` if the widget should appear before the label.
Example:
.. code-block:: html+django
{% if field|form_field_has_label_first %}
...
{% endif %}
"""
return not is_field_checkbox(field)
@register.filter
def get_fieldsets(form):
"""Normalize and iterate over fieldsets in a form.
This will loop through the fieldsets on a given form, converting either
standard Django style or legay Djblets style fieldset data into a standard
form and returning it to the template.
Args:
form (django.forms.Form):
The form containing the fieldsets.
Yields:
tuple:
A tuple of (fieldset_title, fieldset_info).
Example:
.. code-block:: html+django
{% for fieldset_title, fieldset in form|get_fieldsets %}
...
{% endfor %}
"""
try:
fieldsets = form.Meta.fieldsets
except AttributeError:
fieldsets = []
for fieldset in fieldsets:
if isinstance(fieldset, tuple):
# This is a standard Django-style fieldset entry. It's a tuple
# of (title, info).
yield fieldset
elif isinstance(fieldset, dict):
# This is a legacy Djblets-style fieldset entry. It's a dictionary
# that may contain the title as a "title" key.
yield fieldset.get('title'), fieldset
else:
raise ValueError('Invalid fieldset value: %r' % fieldset)