|
0
|
1 |
""" |
|
|
2 |
Form classes |
|
|
3 |
""" |
|
|
4 |
|
|
29
|
5 |
from django.core.exceptions import ValidationError |
|
|
6 |
from django.utils.copycompat import deepcopy |
|
0
|
7 |
from django.utils.datastructures import SortedDict |
|
|
8 |
from django.utils.html import conditional_escape |
|
|
9 |
from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode |
|
|
10 |
from django.utils.safestring import mark_safe |
|
|
11 |
|
|
|
12 |
from fields import Field, FileField |
|
|
13 |
from widgets import Media, media_property, TextInput, Textarea |
|
29
|
14 |
from util import flatatt, ErrorDict, ErrorList |
|
0
|
15 |
|
|
|
16 |
__all__ = ('BaseForm', 'Form') |
|
|
17 |
|
|
|
18 |
NON_FIELD_ERRORS = '__all__' |
|
|
19 |
|
|
|
20 |
def pretty_name(name): |
|
29
|
21 |
"""Converts 'first_name' to 'First name'""" |
|
|
22 |
if not name: |
|
|
23 |
return u'' |
|
|
24 |
return name.replace('_', ' ').capitalize() |
|
0
|
25 |
|
|
|
26 |
def get_declared_fields(bases, attrs, with_base_fields=True): |
|
|
27 |
""" |
|
|
28 |
Create a list of form field instances from the passed in 'attrs', plus any |
|
|
29 |
similar fields on the base classes (in 'bases'). This is used by both the |
|
|
30 |
Form and ModelForm metclasses. |
|
|
31 |
|
|
|
32 |
If 'with_base_fields' is True, all fields from the bases are used. |
|
|
33 |
Otherwise, only fields in the 'declared_fields' attribute on the bases are |
|
|
34 |
used. The distinction is useful in ModelForm subclassing. |
|
|
35 |
Also integrates any additional media definitions |
|
|
36 |
""" |
|
|
37 |
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] |
|
|
38 |
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) |
|
|
39 |
|
|
|
40 |
# If this class is subclassing another Form, add that Form's fields. |
|
|
41 |
# Note that we loop over the bases in *reverse*. This is necessary in |
|
|
42 |
# order to preserve the correct order of fields. |
|
|
43 |
if with_base_fields: |
|
|
44 |
for base in bases[::-1]: |
|
|
45 |
if hasattr(base, 'base_fields'): |
|
|
46 |
fields = base.base_fields.items() + fields |
|
|
47 |
else: |
|
|
48 |
for base in bases[::-1]: |
|
|
49 |
if hasattr(base, 'declared_fields'): |
|
|
50 |
fields = base.declared_fields.items() + fields |
|
|
51 |
|
|
|
52 |
return SortedDict(fields) |
|
|
53 |
|
|
|
54 |
class DeclarativeFieldsMetaclass(type): |
|
|
55 |
""" |
|
|
56 |
Metaclass that converts Field attributes to a dictionary called |
|
|
57 |
'base_fields', taking into account parent class 'base_fields' as well. |
|
|
58 |
""" |
|
|
59 |
def __new__(cls, name, bases, attrs): |
|
|
60 |
attrs['base_fields'] = get_declared_fields(bases, attrs) |
|
|
61 |
new_class = super(DeclarativeFieldsMetaclass, |
|
|
62 |
cls).__new__(cls, name, bases, attrs) |
|
|
63 |
if 'media' not in attrs: |
|
|
64 |
new_class.media = media_property(new_class) |
|
|
65 |
return new_class |
|
|
66 |
|
|
|
67 |
class BaseForm(StrAndUnicode): |
|
|
68 |
# This is the main implementation of all the Form logic. Note that this |
|
|
69 |
# class is different than Form. See the comments by the Form class for more |
|
|
70 |
# information. Any improvements to the form API should be made to *this* |
|
|
71 |
# class, not to the Form class. |
|
|
72 |
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, |
|
|
73 |
initial=None, error_class=ErrorList, label_suffix=':', |
|
|
74 |
empty_permitted=False): |
|
|
75 |
self.is_bound = data is not None or files is not None |
|
|
76 |
self.data = data or {} |
|
|
77 |
self.files = files or {} |
|
|
78 |
self.auto_id = auto_id |
|
|
79 |
self.prefix = prefix |
|
|
80 |
self.initial = initial or {} |
|
|
81 |
self.error_class = error_class |
|
|
82 |
self.label_suffix = label_suffix |
|
|
83 |
self.empty_permitted = empty_permitted |
|
|
84 |
self._errors = None # Stores the errors after clean() has been called. |
|
|
85 |
self._changed_data = None |
|
|
86 |
|
|
|
87 |
# The base_fields class attribute is the *class-wide* definition of |
|
|
88 |
# fields. Because a particular *instance* of the class might want to |
|
|
89 |
# alter self.fields, we create self.fields here by copying base_fields. |
|
|
90 |
# Instances should always modify self.fields; they should not modify |
|
|
91 |
# self.base_fields. |
|
|
92 |
self.fields = deepcopy(self.base_fields) |
|
|
93 |
|
|
|
94 |
def __unicode__(self): |
|
|
95 |
return self.as_table() |
|
|
96 |
|
|
|
97 |
def __iter__(self): |
|
|
98 |
for name, field in self.fields.items(): |
|
|
99 |
yield BoundField(self, field, name) |
|
|
100 |
|
|
|
101 |
def __getitem__(self, name): |
|
|
102 |
"Returns a BoundField with the given name." |
|
|
103 |
try: |
|
|
104 |
field = self.fields[name] |
|
|
105 |
except KeyError: |
|
|
106 |
raise KeyError('Key %r not found in Form' % name) |
|
|
107 |
return BoundField(self, field, name) |
|
|
108 |
|
|
|
109 |
def _get_errors(self): |
|
|
110 |
"Returns an ErrorDict for the data provided for the form" |
|
|
111 |
if self._errors is None: |
|
|
112 |
self.full_clean() |
|
|
113 |
return self._errors |
|
|
114 |
errors = property(_get_errors) |
|
|
115 |
|
|
|
116 |
def is_valid(self): |
|
|
117 |
""" |
|
|
118 |
Returns True if the form has no errors. Otherwise, False. If errors are |
|
|
119 |
being ignored, returns False. |
|
|
120 |
""" |
|
|
121 |
return self.is_bound and not bool(self.errors) |
|
|
122 |
|
|
|
123 |
def add_prefix(self, field_name): |
|
|
124 |
""" |
|
|
125 |
Returns the field name with a prefix appended, if this Form has a |
|
|
126 |
prefix set. |
|
|
127 |
|
|
|
128 |
Subclasses may wish to override. |
|
|
129 |
""" |
|
|
130 |
return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name |
|
|
131 |
|
|
|
132 |
def add_initial_prefix(self, field_name): |
|
|
133 |
""" |
|
|
134 |
Add a 'initial' prefix for checking dynamic initial values |
|
|
135 |
""" |
|
|
136 |
return u'initial-%s' % self.add_prefix(field_name) |
|
|
137 |
|
|
|
138 |
def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row): |
|
|
139 |
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." |
|
|
140 |
top_errors = self.non_field_errors() # Errors that should be displayed above all fields. |
|
|
141 |
output, hidden_fields = [], [] |
|
29
|
142 |
|
|
0
|
143 |
for name, field in self.fields.items(): |
|
29
|
144 |
html_class_attr = '' |
|
0
|
145 |
bf = BoundField(self, field, name) |
|
|
146 |
bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable. |
|
|
147 |
if bf.is_hidden: |
|
|
148 |
if bf_errors: |
|
|
149 |
top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) |
|
|
150 |
hidden_fields.append(unicode(bf)) |
|
|
151 |
else: |
|
29
|
152 |
# Create a 'class="..."' atribute if the row should have any |
|
|
153 |
# CSS classes applied. |
|
|
154 |
css_classes = bf.css_classes() |
|
|
155 |
if css_classes: |
|
|
156 |
html_class_attr = ' class="%s"' % css_classes |
|
|
157 |
|
|
0
|
158 |
if errors_on_separate_row and bf_errors: |
|
|
159 |
output.append(error_row % force_unicode(bf_errors)) |
|
29
|
160 |
|
|
0
|
161 |
if bf.label: |
|
|
162 |
label = conditional_escape(force_unicode(bf.label)) |
|
|
163 |
# Only add the suffix if the label does not end in |
|
|
164 |
# punctuation. |
|
|
165 |
if self.label_suffix: |
|
|
166 |
if label[-1] not in ':?.!': |
|
|
167 |
label += self.label_suffix |
|
|
168 |
label = bf.label_tag(label) or '' |
|
|
169 |
else: |
|
|
170 |
label = '' |
|
29
|
171 |
|
|
0
|
172 |
if field.help_text: |
|
|
173 |
help_text = help_text_html % force_unicode(field.help_text) |
|
|
174 |
else: |
|
|
175 |
help_text = u'' |
|
29
|
176 |
|
|
|
177 |
output.append(normal_row % { |
|
|
178 |
'errors': force_unicode(bf_errors), |
|
|
179 |
'label': force_unicode(label), |
|
|
180 |
'field': unicode(bf), |
|
|
181 |
'help_text': help_text, |
|
|
182 |
'html_class_attr': html_class_attr |
|
|
183 |
}) |
|
|
184 |
|
|
0
|
185 |
if top_errors: |
|
|
186 |
output.insert(0, error_row % force_unicode(top_errors)) |
|
29
|
187 |
|
|
0
|
188 |
if hidden_fields: # Insert any hidden fields in the last row. |
|
|
189 |
str_hidden = u''.join(hidden_fields) |
|
|
190 |
if output: |
|
|
191 |
last_row = output[-1] |
|
|
192 |
# Chop off the trailing row_ender (e.g. '</td></tr>') and |
|
|
193 |
# insert the hidden fields. |
|
|
194 |
if not last_row.endswith(row_ender): |
|
|
195 |
# This can happen in the as_p() case (and possibly others |
|
|
196 |
# that users write): if there are only top errors, we may |
|
|
197 |
# not be able to conscript the last row for our purposes, |
|
|
198 |
# so insert a new, empty row. |
|
29
|
199 |
last_row = (normal_row % {'errors': '', 'label': '', |
|
|
200 |
'field': '', 'help_text':'', |
|
|
201 |
'html_class_attr': html_class_attr}) |
|
0
|
202 |
output.append(last_row) |
|
|
203 |
output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender |
|
|
204 |
else: |
|
|
205 |
# If there aren't any rows in the output, just append the |
|
|
206 |
# hidden fields. |
|
|
207 |
output.append(str_hidden) |
|
|
208 |
return mark_safe(u'\n'.join(output)) |
|
|
209 |
|
|
|
210 |
def as_table(self): |
|
|
211 |
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>." |
|
29
|
212 |
return self._html_output( |
|
|
213 |
normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', |
|
|
214 |
error_row = u'<tr><td colspan="2">%s</td></tr>', |
|
|
215 |
row_ender = u'</td></tr>', |
|
|
216 |
help_text_html = u'<br />%s', |
|
|
217 |
errors_on_separate_row = False) |
|
0
|
218 |
|
|
|
219 |
def as_ul(self): |
|
|
220 |
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." |
|
29
|
221 |
return self._html_output( |
|
|
222 |
normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', |
|
|
223 |
error_row = u'<li>%s</li>', |
|
|
224 |
row_ender = '</li>', |
|
|
225 |
help_text_html = u' %s', |
|
|
226 |
errors_on_separate_row = False) |
|
0
|
227 |
|
|
|
228 |
def as_p(self): |
|
|
229 |
"Returns this form rendered as HTML <p>s." |
|
29
|
230 |
return self._html_output( |
|
|
231 |
normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', |
|
|
232 |
error_row = u'%s', |
|
|
233 |
row_ender = '</p>', |
|
|
234 |
help_text_html = u' %s', |
|
|
235 |
errors_on_separate_row = True) |
|
0
|
236 |
|
|
|
237 |
def non_field_errors(self): |
|
|
238 |
""" |
|
|
239 |
Returns an ErrorList of errors that aren't associated with a particular |
|
|
240 |
field -- i.e., from Form.clean(). Returns an empty ErrorList if there |
|
|
241 |
are none. |
|
|
242 |
""" |
|
|
243 |
return self.errors.get(NON_FIELD_ERRORS, self.error_class()) |
|
|
244 |
|
|
|
245 |
def _raw_value(self, fieldname): |
|
|
246 |
""" |
|
|
247 |
Returns the raw_value for a particular field name. This is just a |
|
|
248 |
convenient wrapper around widget.value_from_datadict. |
|
|
249 |
""" |
|
|
250 |
field = self.fields[fieldname] |
|
|
251 |
prefix = self.add_prefix(fieldname) |
|
|
252 |
return field.widget.value_from_datadict(self.data, self.files, prefix) |
|
|
253 |
|
|
|
254 |
def full_clean(self): |
|
|
255 |
""" |
|
|
256 |
Cleans all of self.data and populates self._errors and |
|
|
257 |
self.cleaned_data. |
|
|
258 |
""" |
|
|
259 |
self._errors = ErrorDict() |
|
|
260 |
if not self.is_bound: # Stop further processing. |
|
|
261 |
return |
|
|
262 |
self.cleaned_data = {} |
|
|
263 |
# If the form is permitted to be empty, and none of the form data has |
|
|
264 |
# changed from the initial data, short circuit any validation. |
|
|
265 |
if self.empty_permitted and not self.has_changed(): |
|
|
266 |
return |
|
29
|
267 |
self._clean_fields() |
|
|
268 |
self._clean_form() |
|
|
269 |
self._post_clean() |
|
|
270 |
if self._errors: |
|
|
271 |
delattr(self, 'cleaned_data') |
|
|
272 |
|
|
|
273 |
def _clean_fields(self): |
|
0
|
274 |
for name, field in self.fields.items(): |
|
|
275 |
# value_from_datadict() gets the data from the data dictionaries. |
|
|
276 |
# Each widget type knows how to retrieve its own data, because some |
|
|
277 |
# widgets split data over several HTML fields. |
|
|
278 |
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) |
|
|
279 |
try: |
|
|
280 |
if isinstance(field, FileField): |
|
|
281 |
initial = self.initial.get(name, field.initial) |
|
|
282 |
value = field.clean(value, initial) |
|
|
283 |
else: |
|
|
284 |
value = field.clean(value) |
|
|
285 |
self.cleaned_data[name] = value |
|
|
286 |
if hasattr(self, 'clean_%s' % name): |
|
|
287 |
value = getattr(self, 'clean_%s' % name)() |
|
|
288 |
self.cleaned_data[name] = value |
|
|
289 |
except ValidationError, e: |
|
|
290 |
self._errors[name] = self.error_class(e.messages) |
|
|
291 |
if name in self.cleaned_data: |
|
|
292 |
del self.cleaned_data[name] |
|
29
|
293 |
|
|
|
294 |
def _clean_form(self): |
|
0
|
295 |
try: |
|
|
296 |
self.cleaned_data = self.clean() |
|
|
297 |
except ValidationError, e: |
|
|
298 |
self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages) |
|
29
|
299 |
|
|
|
300 |
def _post_clean(self): |
|
|
301 |
""" |
|
|
302 |
An internal hook for performing additional cleaning after form cleaning |
|
|
303 |
is complete. Used for model validation in model forms. |
|
|
304 |
""" |
|
|
305 |
pass |
|
0
|
306 |
|
|
|
307 |
def clean(self): |
|
|
308 |
""" |
|
|
309 |
Hook for doing any extra form-wide cleaning after Field.clean() been |
|
|
310 |
called on every field. Any ValidationError raised by this method will |
|
|
311 |
not be associated with a particular field; it will have a special-case |
|
|
312 |
association with the field named '__all__'. |
|
|
313 |
""" |
|
|
314 |
return self.cleaned_data |
|
|
315 |
|
|
|
316 |
def has_changed(self): |
|
|
317 |
""" |
|
|
318 |
Returns True if data differs from initial. |
|
|
319 |
""" |
|
|
320 |
return bool(self.changed_data) |
|
|
321 |
|
|
|
322 |
def _get_changed_data(self): |
|
|
323 |
if self._changed_data is None: |
|
|
324 |
self._changed_data = [] |
|
|
325 |
# XXX: For now we're asking the individual widgets whether or not the |
|
|
326 |
# data has changed. It would probably be more efficient to hash the |
|
|
327 |
# initial data, store it in a hidden field, and compare a hash of the |
|
|
328 |
# submitted data, but we'd need a way to easily get the string value |
|
|
329 |
# for a given field. Right now, that logic is embedded in the render |
|
|
330 |
# method of each widget. |
|
|
331 |
for name, field in self.fields.items(): |
|
|
332 |
prefixed_name = self.add_prefix(name) |
|
|
333 |
data_value = field.widget.value_from_datadict(self.data, self.files, prefixed_name) |
|
|
334 |
if not field.show_hidden_initial: |
|
|
335 |
initial_value = self.initial.get(name, field.initial) |
|
|
336 |
else: |
|
|
337 |
initial_prefixed_name = self.add_initial_prefix(name) |
|
|
338 |
hidden_widget = field.hidden_widget() |
|
|
339 |
initial_value = hidden_widget.value_from_datadict( |
|
|
340 |
self.data, self.files, initial_prefixed_name) |
|
|
341 |
if field.widget._has_changed(initial_value, data_value): |
|
|
342 |
self._changed_data.append(name) |
|
|
343 |
return self._changed_data |
|
|
344 |
changed_data = property(_get_changed_data) |
|
|
345 |
|
|
|
346 |
def _get_media(self): |
|
|
347 |
""" |
|
|
348 |
Provide a description of all media required to render the widgets on this form |
|
|
349 |
""" |
|
|
350 |
media = Media() |
|
|
351 |
for field in self.fields.values(): |
|
|
352 |
media = media + field.widget.media |
|
|
353 |
return media |
|
|
354 |
media = property(_get_media) |
|
|
355 |
|
|
|
356 |
def is_multipart(self): |
|
|
357 |
""" |
|
|
358 |
Returns True if the form needs to be multipart-encrypted, i.e. it has |
|
|
359 |
FileInput. Otherwise, False. |
|
|
360 |
""" |
|
|
361 |
for field in self.fields.values(): |
|
|
362 |
if field.widget.needs_multipart_form: |
|
|
363 |
return True |
|
|
364 |
return False |
|
|
365 |
|
|
|
366 |
def hidden_fields(self): |
|
|
367 |
""" |
|
|
368 |
Returns a list of all the BoundField objects that are hidden fields. |
|
|
369 |
Useful for manual form layout in templates. |
|
|
370 |
""" |
|
|
371 |
return [field for field in self if field.is_hidden] |
|
|
372 |
|
|
|
373 |
def visible_fields(self): |
|
|
374 |
""" |
|
|
375 |
Returns a list of BoundField objects that aren't hidden fields. |
|
|
376 |
The opposite of the hidden_fields() method. |
|
|
377 |
""" |
|
|
378 |
return [field for field in self if not field.is_hidden] |
|
|
379 |
|
|
|
380 |
class Form(BaseForm): |
|
|
381 |
"A collection of Fields, plus their associated data." |
|
|
382 |
# This is a separate class from BaseForm in order to abstract the way |
|
|
383 |
# self.fields is specified. This class (Form) is the one that does the |
|
|
384 |
# fancy metaclass stuff purely for the semantic sugar -- it allows one |
|
|
385 |
# to define a form using declarative syntax. |
|
|
386 |
# BaseForm itself has no way of designating self.fields. |
|
|
387 |
__metaclass__ = DeclarativeFieldsMetaclass |
|
|
388 |
|
|
|
389 |
class BoundField(StrAndUnicode): |
|
|
390 |
"A Field plus data" |
|
|
391 |
def __init__(self, form, field, name): |
|
|
392 |
self.form = form |
|
|
393 |
self.field = field |
|
|
394 |
self.name = name |
|
|
395 |
self.html_name = form.add_prefix(name) |
|
|
396 |
self.html_initial_name = form.add_initial_prefix(name) |
|
29
|
397 |
self.html_initial_id = form.add_initial_prefix(self.auto_id) |
|
0
|
398 |
if self.field.label is None: |
|
|
399 |
self.label = pretty_name(name) |
|
|
400 |
else: |
|
|
401 |
self.label = self.field.label |
|
|
402 |
self.help_text = field.help_text or '' |
|
|
403 |
|
|
|
404 |
def __unicode__(self): |
|
|
405 |
"""Renders this field as an HTML widget.""" |
|
|
406 |
if self.field.show_hidden_initial: |
|
|
407 |
return self.as_widget() + self.as_hidden(only_initial=True) |
|
|
408 |
return self.as_widget() |
|
|
409 |
|
|
|
410 |
def _errors(self): |
|
|
411 |
""" |
|
|
412 |
Returns an ErrorList for this field. Returns an empty ErrorList |
|
|
413 |
if there are none. |
|
|
414 |
""" |
|
|
415 |
return self.form.errors.get(self.name, self.form.error_class()) |
|
|
416 |
errors = property(_errors) |
|
|
417 |
|
|
|
418 |
def as_widget(self, widget=None, attrs=None, only_initial=False): |
|
|
419 |
""" |
|
|
420 |
Renders the field by rendering the passed widget, adding any HTML |
|
|
421 |
attributes passed as attrs. If no widget is specified, then the |
|
|
422 |
field's default widget will be used. |
|
|
423 |
""" |
|
|
424 |
if not widget: |
|
|
425 |
widget = self.field.widget |
|
|
426 |
attrs = attrs or {} |
|
|
427 |
auto_id = self.auto_id |
|
|
428 |
if auto_id and 'id' not in attrs and 'id' not in widget.attrs: |
|
29
|
429 |
if not only_initial: |
|
|
430 |
attrs['id'] = auto_id |
|
|
431 |
else: |
|
|
432 |
attrs['id'] = self.html_initial_id |
|
0
|
433 |
if not self.form.is_bound: |
|
|
434 |
data = self.form.initial.get(self.name, self.field.initial) |
|
|
435 |
if callable(data): |
|
|
436 |
data = data() |
|
|
437 |
else: |
|
|
438 |
if isinstance(self.field, FileField) and self.data is None: |
|
|
439 |
data = self.form.initial.get(self.name, self.field.initial) |
|
|
440 |
else: |
|
|
441 |
data = self.data |
|
|
442 |
if not only_initial: |
|
|
443 |
name = self.html_name |
|
|
444 |
else: |
|
|
445 |
name = self.html_initial_name |
|
29
|
446 |
if self.field.localize: |
|
|
447 |
data = self.field.localize_value(data) |
|
0
|
448 |
return widget.render(name, data, attrs=attrs) |
|
|
449 |
|
|
|
450 |
def as_text(self, attrs=None, **kwargs): |
|
|
451 |
""" |
|
|
452 |
Returns a string of HTML for representing this as an <input type="text">. |
|
|
453 |
""" |
|
|
454 |
return self.as_widget(TextInput(), attrs, **kwargs) |
|
|
455 |
|
|
|
456 |
def as_textarea(self, attrs=None, **kwargs): |
|
|
457 |
"Returns a string of HTML for representing this as a <textarea>." |
|
|
458 |
return self.as_widget(Textarea(), attrs, **kwargs) |
|
|
459 |
|
|
|
460 |
def as_hidden(self, attrs=None, **kwargs): |
|
|
461 |
""" |
|
|
462 |
Returns a string of HTML for representing this as an <input type="hidden">. |
|
|
463 |
""" |
|
|
464 |
return self.as_widget(self.field.hidden_widget(), attrs, **kwargs) |
|
|
465 |
|
|
|
466 |
def _data(self): |
|
|
467 |
""" |
|
|
468 |
Returns the data for this BoundField, or None if it wasn't given. |
|
|
469 |
""" |
|
|
470 |
return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name) |
|
|
471 |
data = property(_data) |
|
|
472 |
|
|
|
473 |
def label_tag(self, contents=None, attrs=None): |
|
|
474 |
""" |
|
|
475 |
Wraps the given contents in a <label>, if the field has an ID attribute. |
|
|
476 |
Does not HTML-escape the contents. If contents aren't given, uses the |
|
|
477 |
field's HTML-escaped label. |
|
|
478 |
|
|
|
479 |
If attrs are given, they're used as HTML attributes on the <label> tag. |
|
|
480 |
""" |
|
|
481 |
contents = contents or conditional_escape(self.label) |
|
|
482 |
widget = self.field.widget |
|
|
483 |
id_ = widget.attrs.get('id') or self.auto_id |
|
|
484 |
if id_: |
|
|
485 |
attrs = attrs and flatatt(attrs) or '' |
|
|
486 |
contents = u'<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents)) |
|
|
487 |
return mark_safe(contents) |
|
|
488 |
|
|
29
|
489 |
def css_classes(self, extra_classes=None): |
|
|
490 |
""" |
|
|
491 |
Returns a string of space-separated CSS classes for this field. |
|
|
492 |
""" |
|
|
493 |
if hasattr(extra_classes, 'split'): |
|
|
494 |
extra_classes = extra_classes.split() |
|
|
495 |
extra_classes = set(extra_classes or []) |
|
|
496 |
if self.errors and hasattr(self.form, 'error_css_class'): |
|
|
497 |
extra_classes.add(self.form.error_css_class) |
|
|
498 |
if self.field.required and hasattr(self.form, 'required_css_class'): |
|
|
499 |
extra_classes.add(self.form.required_css_class) |
|
|
500 |
return ' '.join(extra_classes) |
|
|
501 |
|
|
0
|
502 |
def _is_hidden(self): |
|
|
503 |
"Returns True if this BoundField's widget is hidden." |
|
|
504 |
return self.field.widget.is_hidden |
|
|
505 |
is_hidden = property(_is_hidden) |
|
|
506 |
|
|
|
507 |
def _auto_id(self): |
|
|
508 |
""" |
|
|
509 |
Calculates and returns the ID attribute for this BoundField, if the |
|
|
510 |
associated Form has specified auto_id. Returns an empty string otherwise. |
|
|
511 |
""" |
|
|
512 |
auto_id = self.form.auto_id |
|
|
513 |
if auto_id and '%s' in smart_unicode(auto_id): |
|
|
514 |
return smart_unicode(auto_id) % self.html_name |
|
|
515 |
elif auto_id: |
|
|
516 |
return self.html_name |
|
|
517 |
return '' |
|
|
518 |
auto_id = property(_auto_id) |