diff --git a/django/newforms/extras/widgets.py b/django/newforms/extras/widgets.py
index e3ef1d7..bd6220d 100644
a
|
b
|
from django.newforms.widgets import Widget, Select
|
9 | 9 | from django.utils.dates import MONTHS |
10 | 10 | from django.utils.safestring import mark_safe |
11 | 11 | |
12 | | __all__ = ('SelectDateWidget',) |
| 12 | __all__ = ('SelectDateWidget', ) |
13 | 13 | |
14 | 14 | RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$') |
15 | 15 | |
| 16 | |
16 | 17 | class SelectDateWidget(Widget): |
17 | 18 | """ |
18 | 19 | A Widget that splits date input into three <select> boxes. |
… |
… |
class SelectDateWidget(Widget):
|
25 | 26 | year_field = '%s_year' |
26 | 27 | |
27 | 28 | def __init__(self, attrs=None, years=None): |
28 | | # years is an optional list/tuple of years to use in the "year" select box. |
| 29 | # years is an optional list/tuple of years to use in the |
| 30 | # "year" select box. |
29 | 31 | self.attrs = attrs or {} |
30 | 32 | if years: |
31 | 33 | self.years = years |
… |
… |
class SelectDateWidget(Widget):
|
41 | 43 | if isinstance(value, basestring): |
42 | 44 | match = RE_DATE.match(value) |
43 | 45 | if match: |
44 | | year_val, month_val, day_val = [int(v) for v in match.groups()] |
| 46 | year_val, month_val, day_val = [ |
| 47 | int(v) for v in match.groups()] |
45 | 48 | |
46 | 49 | output = [] |
47 | 50 | |
… |
… |
class SelectDateWidget(Widget):
|
53 | 56 | month_choices = MONTHS.items() |
54 | 57 | month_choices.sort() |
55 | 58 | local_attrs = self.build_attrs(id=self.month_field % id_) |
56 | | select_html = Select(choices=month_choices).render(self.month_field % name, month_val, local_attrs) |
| 59 | select_html = Select(choices=month_choices).render( |
| 60 | self.month_field % name, month_val, local_attrs) |
57 | 61 | output.append(select_html) |
58 | 62 | |
59 | 63 | day_choices = [(i, i) for i in range(1, 32)] |
60 | 64 | local_attrs['id'] = self.day_field % id_ |
61 | | select_html = Select(choices=day_choices).render(self.day_field % name, day_val, local_attrs) |
| 65 | select_html = Select(choices=day_choices).render( |
| 66 | self.day_field % name, day_val, local_attrs) |
62 | 67 | output.append(select_html) |
63 | 68 | |
64 | 69 | year_choices = [(i, i) for i in self.years] |
65 | 70 | local_attrs['id'] = self.year_field % id_ |
66 | | select_html = Select(choices=year_choices).render(self.year_field % name, year_val, local_attrs) |
| 71 | select_html = Select(choices=year_choices).render( |
| 72 | self.year_field % name, year_val, local_attrs) |
67 | 73 | output.append(select_html) |
68 | 74 | |
69 | 75 | return mark_safe(u'\n'.join(output)) |
… |
… |
class SelectDateWidget(Widget):
|
73 | 79 | id_for_label = classmethod(id_for_label) |
74 | 80 | |
75 | 81 | def value_from_datadict(self, data, files, name): |
76 | | y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name) |
| 82 | y = data.get(self.year_field % name) |
| 83 | m = data.get(self.month_field % name) |
| 84 | d = data.get(self.day_field % name) |
77 | 85 | if y and m and d: |
78 | 86 | return '%s-%s-%s' % (y, m, d) |
79 | 87 | return data.get(name, None) |
diff --git a/django/newforms/fields.py b/django/newforms/fields.py
index dfe46a2..6d74c74 100644
a
|
b
|
from django.utils.translation import ugettext_lazy as _
|
21 | 21 | from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str |
22 | 22 | |
23 | 23 | from util import ErrorList, ValidationError |
24 | | from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput |
| 24 | from widgets import TextInput, PasswordInput, HiddenInput |
| 25 | from widgets import MultipleHiddenInput, FileInput, CheckboxInput, Select |
| 26 | from widgets import NullBooleanSelect, SelectMultiple, DateTimeInput |
25 | 27 | |
26 | 28 | |
27 | 29 | __all__ = ( |
… |
… |
EMPTY_VALUES = (None, '')
|
40 | 42 | |
41 | 43 | |
42 | 44 | class Field(object): |
43 | | widget = TextInput # Default widget to use when rendering this type of Field. |
44 | | hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". |
| 45 | # Default widget to use when rendering this type of Field. |
| 46 | widget = TextInput |
| 47 | # Default widget to use when rendering this as "hidden". |
| 48 | hidden_widget = HiddenInput |
45 | 49 | default_error_messages = { |
46 | 50 | 'required': _(u'This field is required.'), |
47 | 51 | 'invalid': _(u'Enter a valid value.'), |
… |
… |
class Field(object):
|
119 | 123 | result.widget = copy.deepcopy(self.widget, memo) |
120 | 124 | return result |
121 | 125 | |
| 126 | |
122 | 127 | class CharField(Field): |
123 | 128 | default_error_messages = { |
124 | 129 | 'max_length': _(u'Ensure this value has at most %(max)d characters (it has %(length)d).'), |
diff --git a/django/newforms/forms.py b/django/newforms/forms.py
index 2c481e4..163a2c2 100644
a
|
b
|
__all__ = ('BaseForm', 'Form')
|
17 | 17 | |
18 | 18 | NON_FIELD_ERRORS = '__all__' |
19 | 19 | |
| 20 | |
20 | 21 | def pretty_name(name): |
21 | 22 | "Converts 'first_name' to 'First name'" |
22 | 23 | name = name[0].upper() + name[1:] |
23 | 24 | return name.replace('_', ' ') |
24 | 25 | |
| 26 | |
25 | 27 | def get_declared_fields(bases, attrs, with_base_fields=True): |
26 | 28 | """ |
27 | 29 | Create a list of form field instances from the passed in 'attrs', plus any |
… |
… |
def get_declared_fields(bases, attrs, with_base_fields=True):
|
32 | 34 | Otherwise, only fields in the 'declared_fields' attribute on the bases are |
33 | 35 | used. The distinction is useful in ModelForm subclassing. |
34 | 36 | """ |
35 | | fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] |
| 37 | fields = [(field_name, attrs.pop(field_name)) \ |
| 38 | for field_name, obj in attrs.items() if isinstance(obj, Field)] |
36 | 39 | fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) |
37 | 40 | |
38 | 41 | # If this class is subclassing another Form, add that Form's fields. |
… |
… |
def get_declared_fields(bases, attrs, with_base_fields=True):
|
49 | 52 | |
50 | 53 | return SortedDict(fields) |
51 | 54 | |
| 55 | |
52 | 56 | class DeclarativeFieldsMetaclass(type): |
53 | 57 | """ |
54 | 58 | Metaclass that converts Field attributes to a dictionary called |
55 | 59 | 'base_fields', taking into account parent class 'base_fields' as well. |
56 | 60 | """ |
| 61 | |
57 | 62 | def __new__(cls, name, bases, attrs): |
58 | 63 | attrs['base_fields'] = get_declared_fields(bases, attrs) |
59 | 64 | return type.__new__(cls, name, bases, attrs) |
60 | 65 | |
| 66 | |
61 | 67 | class BaseForm(StrAndUnicode): |
62 | | # This is the main implementation of all the Form logic. Note that this |
63 | | # class is different than Form. See the comments by the Form class for more |
64 | | # information. Any improvements to the form API should be made to *this* |
65 | | # class, not to the Form class. |
| 68 | """ |
| 69 | This is the main implementation of all the Form logic. Note that this |
| 70 | class is different than Form. See the comments by the Form class for more |
| 71 | information. Any improvements to the form API should be made to *this* |
| 72 | class, not to the Form class. |
| 73 | """ |
| 74 | |
66 | 75 | def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, |
67 | 76 | initial=None, error_class=ErrorList, label_suffix=':'): |
68 | 77 | self.is_bound = data is not None or files is not None |
… |
… |
class BaseForm(StrAndUnicode):
|
118 | 127 | |
119 | 128 | Subclasses may wish to override. |
120 | 129 | """ |
121 | | return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name |
| 130 | return self.prefix and ('%s-%s' % (self.prefix, field_name)) or \ |
| 131 | field_name |
122 | 132 | |
123 | | def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row): |
124 | | "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." |
125 | | top_errors = self.non_field_errors() # Errors that should be displayed above all fields. |
| 133 | def _html_output(self, normal_row, error_row, row_ender, |
| 134 | help_text_html, errors_on_separate_row): |
| 135 | """ |
| 136 | Helper function for outputting HTML. Used by as_table(), |
| 137 | as_ul(), as_p(). |
| 138 | """ |
| 139 | # Errors that should be displayed above all fields. |
| 140 | top_errors = self.non_field_errors() |
126 | 141 | output, hidden_fields = [], [] |
127 | 142 | for name, field in self.fields.items(): |
128 | 143 | bf = BoundField(self, field, name) |
129 | | bf_errors = self.error_class([escape(error) for error in bf.errors]) # Escape and cache in local variable. |
| 144 | # Escape and cache in local variable. |
| 145 | bf_errors = self.error_class([escape(error) \ |
| 146 | for error in bf.errors]) |
130 | 147 | if bf.is_hidden: |
131 | 148 | if bf_errors: |
132 | | top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) |
| 149 | top_errors.extend([u'(Hidden field %s) %s' % ( |
| 150 | name, force_unicode(e)) for e in bf_errors]) |
133 | 151 | hidden_fields.append(unicode(bf)) |
134 | 152 | else: |
135 | 153 | if errors_on_separate_row and bf_errors: |
… |
… |
class BaseForm(StrAndUnicode):
|
148 | 166 | help_text = help_text_html % force_unicode(field.help_text) |
149 | 167 | else: |
150 | 168 | help_text = u'' |
151 | | output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text}) |
| 169 | output.append(normal_row % { |
| 170 | 'errors': force_unicode(bf_errors), |
| 171 | 'label': force_unicode(label), |
| 172 | 'field': unicode(bf), |
| 173 | 'help_text': help_text}) |
152 | 174 | if top_errors: |
153 | 175 | output.insert(0, error_row % force_unicode(top_errors)) |
154 | 176 | if hidden_fields: # Insert any hidden fields in the last row. |
… |
… |
class BaseForm(StrAndUnicode):
|
157 | 179 | last_row = output[-1] |
158 | 180 | # Chop off the trailing row_ender (e.g. '</td></tr>') and |
159 | 181 | # insert the hidden fields. |
160 | | output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender |
| 182 | output[-1] = last_row[:-len(row_ender)] + str_hidden + \ |
| 183 | row_ender |
161 | 184 | else: |
162 | 185 | # If there aren't any rows in the output, just append the |
163 | 186 | # hidden fields. |
… |
… |
class BaseForm(StrAndUnicode):
|
165 | 188 | return mark_safe(u'\n'.join(output)) |
166 | 189 | |
167 | 190 | def as_table(self): |
168 | | "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." |
169 | | 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) |
| 191 | """ |
| 192 | Returns this form rendered as HTML <tr>s -- excluding the |
| 193 | <table></table>. |
| 194 | """ |
| 195 | return self._html_output( |
| 196 | u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s\ |
| 197 | </td></tr>', |
| 198 | u'<tr><td colspan="2">%s</td></tr>', |
| 199 | '</td></tr>', |
| 200 | u'<br />%s', |
| 201 | False) |
170 | 202 | |
171 | 203 | def as_ul(self): |
172 | 204 | "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." |
173 | | return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False) |
| 205 | return self._html_output( |
| 206 | u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', |
| 207 | u'<li>%s</li>', |
| 208 | '</li>', |
| 209 | u' %s', |
| 210 | False) |
174 | 211 | |
175 | 212 | def as_p(self): |
176 | 213 | "Returns this form rendered as HTML <p>s." |
177 | | return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True) |
| 214 | return self._html_output( |
| 215 | u'<p>%(label)s %(field)s%(help_text)s</p>', |
| 216 | u'%s', |
| 217 | '</p>', |
| 218 | u' %s', |
| 219 | True) |
178 | 220 | |
179 | 221 | def non_field_errors(self): |
180 | 222 | """ |
… |
… |
class BaseForm(StrAndUnicode):
|
197 | 239 | # value_from_datadict() gets the data from the data dictionaries. |
198 | 240 | # Each widget type knows how to retrieve its own data, because some |
199 | 241 | # widgets split data over several HTML fields. |
200 | | value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) |
| 242 | value = field.widget.value_from_datadict(self.data, |
| 243 | self.files, self.add_prefix(name)) |
201 | 244 | try: |
202 | 245 | if isinstance(field, FileField): |
203 | 246 | initial = self.initial.get(name, field.initial) |
… |
… |
class BaseForm(StrAndUnicode):
|
238 | 281 | return True |
239 | 282 | return False |
240 | 283 | |
| 284 | |
241 | 285 | class Form(BaseForm): |
242 | 286 | "A collection of Fields, plus their associated data." |
243 | 287 | # This is a separate class from BaseForm in order to abstract the way |
… |
… |
class Form(BaseForm):
|
247 | 291 | # BaseForm itself has no way of designating self.fields. |
248 | 292 | __metaclass__ = DeclarativeFieldsMetaclass |
249 | 293 | |
| 294 | |
250 | 295 | class BoundField(StrAndUnicode): |
251 | 296 | "A Field plus data" |
| 297 | |
252 | 298 | def __init__(self, form, field, name): |
253 | 299 | self.form = form |
254 | 300 | self.field = field |
… |
… |
class BoundField(StrAndUnicode):
|
294 | 340 | |
295 | 341 | def as_text(self, attrs=None): |
296 | 342 | """ |
297 | | Returns a string of HTML for representing this as an <input type="text">. |
| 343 | Returns a string of HTML for representing this as |
| 344 | an <input type="text">. |
298 | 345 | """ |
299 | 346 | return self.as_widget(TextInput(), attrs) |
300 | 347 | |
… |
… |
class BoundField(StrAndUnicode):
|
304 | 351 | |
305 | 352 | def as_hidden(self, attrs=None): |
306 | 353 | """ |
307 | | Returns a string of HTML for representing this as an <input type="hidden">. |
| 354 | Returns a string of HTML for representing this as an |
| 355 | <input type="hidden">. |
308 | 356 | """ |
309 | 357 | return self.as_widget(self.field.hidden_widget(), attrs) |
310 | 358 | |
… |
… |
class BoundField(StrAndUnicode):
|
312 | 360 | """ |
313 | 361 | Returns the data for this BoundField, or None if it wasn't given. |
314 | 362 | """ |
315 | | return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name) |
| 363 | return self.field.widget.value_from_datadict( |
| 364 | self.form.data, self.form.files, self.html_name) |
316 | 365 | data = property(_data) |
317 | 366 | |
318 | 367 | def label_tag(self, contents=None, attrs=None): |
319 | 368 | """ |
320 | | Wraps the given contents in a <label>, if the field has an ID attribute. |
321 | | Does not HTML-escape the contents. If contents aren't given, uses the |
322 | | field's HTML-escaped label. |
| 369 | Wraps the given contents in a <label>, if the field has an ID |
| 370 | attribute. Does not HTML-escape the contents. If contents aren't |
| 371 | given, uses the field's HTML-escaped label. |
323 | 372 | |
324 | | If attrs are given, they're used as HTML attributes on the <label> tag. |
| 373 | If attrs are given, they're used as HTML attributes on the |
| 374 | <label> tag. |
325 | 375 | """ |
326 | 376 | contents = contents or escape(self.label) |
327 | 377 | widget = self.field.widget |
328 | 378 | id_ = widget.attrs.get('id') or self.auto_id |
329 | 379 | if id_: |
330 | 380 | attrs = attrs and flatatt(attrs) or '' |
331 | | contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents) |
| 381 | contents = '<label for="%s"%s>%s</label>' % ( |
| 382 | widget.id_for_label(id_), attrs, contents) |
332 | 383 | return mark_safe(contents) |
333 | 384 | |
334 | 385 | def _is_hidden(self): |
… |
… |
class BoundField(StrAndUnicode):
|
339 | 390 | def _auto_id(self): |
340 | 391 | """ |
341 | 392 | Calculates and returns the ID attribute for this BoundField, if the |
342 | | associated Form has specified auto_id. Returns an empty string otherwise. |
| 393 | associated Form has specified auto_id. Returns an empty string |
| 394 | otherwise. |
343 | 395 | """ |
344 | 396 | auto_id = self.form.auto_id |
345 | 397 | if auto_id and '%s' in smart_unicode(auto_id): |
diff --git a/django/newforms/widgets.py b/django/newforms/widgets.py
index ebbf2ab..6e7e6d0 100644
a
|
b
|
__all__ = (
|
25 | 25 | 'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget', |
26 | 26 | ) |
27 | 27 | |
| 28 | |
28 | 29 | class Widget(object): |
29 | | is_hidden = False # Determines whether this corresponds to an <input type="hidden">. |
30 | | needs_multipart_form = False # Determines does this widget need multipart-encrypted form |
| 30 | is_hidden = False # Determines whether this corresponds to |
| 31 | #an <input type="hidden">. |
| 32 | needs_multipart_form = False # Determines does this widget |
| 33 | # need multipart-encrypted form |
31 | 34 | |
32 | 35 | def __init__(self, attrs=None): |
33 | 36 | if attrs is not None: |
… |
… |
class Widget(object):
|
77 | 80 | return id_ |
78 | 81 | id_for_label = classmethod(id_for_label) |
79 | 82 | |
| 83 | |
80 | 84 | class Input(Widget): |
81 | 85 | """ |
82 | 86 | Base class for all <input> widgets (except type='checkbox' and |
… |
… |
class Input(Widget):
|
85 | 89 | input_type = None # Subclasses must define this. |
86 | 90 | |
87 | 91 | def render(self, name, value, attrs=None): |
88 | | if value is None: value = '' |
| 92 | if value is None: |
| 93 | value = '' |
89 | 94 | final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) |
90 | 95 | if value != '': |
91 | 96 | # Only add the 'value' attribute if a value is non-empty. |
92 | 97 | final_attrs['value'] = force_unicode(value) |
93 | 98 | return mark_safe(u'<input%s />' % flatatt(final_attrs)) |
94 | 99 | |
| 100 | |
95 | 101 | class TextInput(Input): |
96 | 102 | input_type = 'text' |
97 | 103 | |
| 104 | |
98 | 105 | class PasswordInput(Input): |
99 | 106 | input_type = 'password' |
100 | 107 | |
… |
… |
class PasswordInput(Input):
|
103 | 110 | self.render_value = render_value |
104 | 111 | |
105 | 112 | def render(self, name, value, attrs=None): |
106 | | if not self.render_value: value=None |
| 113 | if not self.render_value: |
| 114 | value=None |
107 | 115 | return super(PasswordInput, self).render(name, value, attrs) |
108 | 116 | |
| 117 | |
109 | 118 | class HiddenInput(Input): |
110 | 119 | input_type = 'hidden' |
111 | 120 | is_hidden = True |
112 | 121 | |
| 122 | |
113 | 123 | class MultipleHiddenInput(HiddenInput): |
114 | 124 | """ |
115 | 125 | A widget that handles <input type="hidden"> for fields that have a list |
116 | 126 | of values. |
117 | 127 | """ |
| 128 | |
118 | 129 | def __init__(self, attrs=None, choices=()): |
119 | 130 | super(MultipleHiddenInput, self).__init__(attrs) |
120 | 131 | # choices can be any iterable |
121 | 132 | self.choices = choices |
122 | 133 | |
123 | 134 | def render(self, name, value, attrs=None, choices=()): |
124 | | if value is None: value = [] |
| 135 | if value is None: |
| 136 | value = [] |
125 | 137 | final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) |
126 | 138 | return mark_safe(u'\n'.join([(u'<input%s />' % |
127 | 139 | flatatt(dict(value=force_unicode(v), **final_attrs))) |
… |
… |
class MultipleHiddenInput(HiddenInput):
|
132 | 144 | return data.getlist(name) |
133 | 145 | return data.get(name, None) |
134 | 146 | |
| 147 | |
135 | 148 | class FileInput(Input): |
136 | 149 | input_type = 'file' |
137 | 150 | needs_multipart_form = True |
… |
… |
class FileInput(Input):
|
143 | 156 | "File widgets take data from FILES, not POST" |
144 | 157 | return files.get(name, None) |
145 | 158 | |
| 159 | |
146 | 160 | class Textarea(Widget): |
| 161 | |
147 | 162 | def __init__(self, attrs=None): |
148 | 163 | # The 'rows' and 'cols' attributes are required for HTML correctness. |
149 | 164 | self.attrs = {'cols': '40', 'rows': '10'} |
… |
… |
class Textarea(Widget):
|
151 | 166 | self.attrs.update(attrs) |
152 | 167 | |
153 | 168 | def render(self, name, value, attrs=None): |
154 | | if value is None: value = '' |
| 169 | if value is None: |
| 170 | value = '' |
155 | 171 | value = force_unicode(value) |
156 | 172 | final_attrs = self.build_attrs(attrs, name=name) |
157 | 173 | return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), |
158 | 174 | conditional_escape(force_unicode(value)))) |
159 | 175 | |
| 176 | |
160 | 177 | class DateTimeInput(Input): |
161 | 178 | input_type = 'text' |
162 | 179 | format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59' |
… |
… |
class DateTimeInput(Input):
|
173 | 190 | value = value.strftime(self.format) |
174 | 191 | return super(DateTimeInput, self).render(name, value, attrs) |
175 | 192 | |
| 193 | |
176 | 194 | class CheckboxInput(Widget): |
| 195 | |
177 | 196 | def __init__(self, attrs=None, check_test=bool): |
178 | 197 | super(CheckboxInput, self).__init__(attrs) |
179 | 198 | # check_test is a callable that takes a value and returns True |
… |
… |
class CheckboxInput(Widget):
|
198 | 217 | # A missing value means False because HTML form submission does not |
199 | 218 | # send results for unselected checkboxes. |
200 | 219 | return False |
201 | | return super(CheckboxInput, self).value_from_datadict(data, files, name) |
| 220 | return super(CheckboxInput, self).value_from_datadict( |
| 221 | data, files, name) |
| 222 | |
202 | 223 | |
203 | 224 | class Select(Widget): |
| 225 | |
204 | 226 | def __init__(self, attrs=None, choices=()): |
205 | 227 | super(Select, self).__init__(attrs) |
206 | 228 | # choices can be any iterable, but we may need to render this widget |
… |
… |
class Select(Widget):
|
209 | 231 | self.choices = list(choices) |
210 | 232 | |
211 | 233 | def render(self, name, value, attrs=None, choices=()): |
212 | | if value is None: value = '' |
| 234 | if value is None: |
| 235 | value = '' |
213 | 236 | final_attrs = self.build_attrs(attrs, name=name) |
214 | 237 | output = [u'<select%s>' % flatatt(final_attrs)] |
215 | 238 | # Normalize to string. |
216 | 239 | str_value = force_unicode(value) |
217 | 240 | for option_value, option_label in chain(self.choices, choices): |
218 | 241 | option_value = force_unicode(option_value) |
219 | | selected_html = (option_value == str_value) and u' selected="selected"' or '' |
| 242 | selected_html = (option_value == str_value) and \ |
| 243 | u' selected="selected"' or '' |
220 | 244 | output.append(u'<option value="%s"%s>%s</option>' % ( |
221 | 245 | escape(option_value), selected_html, |
222 | 246 | conditional_escape(force_unicode(option_label)))) |
223 | 247 | output.append(u'</select>') |
224 | 248 | return mark_safe(u'\n'.join(output)) |
225 | 249 | |
| 250 | |
226 | 251 | class NullBooleanSelect(Select): |
227 | 252 | """ |
228 | 253 | A Select Widget intended to be used with NullBooleanField. |
229 | 254 | """ |
| 255 | |
230 | 256 | def __init__(self, attrs=None): |
231 | | choices = ((u'1', ugettext('Unknown')), (u'2', ugettext('Yes')), (u'3', ugettext('No'))) |
| 257 | choices = ((u'1', ugettext('Unknown')), (u'2', ugettext('Yes')), |
| 258 | (u'3', ugettext('No'))) |
232 | 259 | super(NullBooleanSelect, self).__init__(attrs, choices) |
233 | 260 | |
234 | 261 | def render(self, name, value, attrs=None, choices=()): |
… |
… |
class NullBooleanSelect(Select):
|
236 | 263 | value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value] |
237 | 264 | except KeyError: |
238 | 265 | value = u'1' |
239 | | return super(NullBooleanSelect, self).render(name, value, attrs, choices) |
| 266 | return super(NullBooleanSelect, self).render( |
| 267 | name, value, attrs, choices) |
240 | 268 | |
241 | 269 | def value_from_datadict(self, data, files, name): |
242 | 270 | value = data.get(name, None) |
243 | | return {u'2': True, u'3': False, True: True, False: False}.get(value, None) |
| 271 | return {u'2': True, u'3': False, True: True, False: False}.get( |
| 272 | value, None) |
| 273 | |
244 | 274 | |
245 | 275 | class SelectMultiple(Widget): |
| 276 | |
246 | 277 | def __init__(self, attrs=None, choices=()): |
247 | 278 | super(SelectMultiple, self).__init__(attrs) |
248 | 279 | # choices can be any iterable |
249 | 280 | self.choices = choices |
250 | 281 | |
251 | 282 | def render(self, name, value, attrs=None, choices=()): |
252 | | if value is None: value = [] |
| 283 | if value is None: |
| 284 | value = [] |
253 | 285 | final_attrs = self.build_attrs(attrs, name=name) |
254 | 286 | output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)] |
255 | | str_values = set([force_unicode(v) for v in value]) # Normalize to strings. |
| 287 | # Normalize to strings. |
| 288 | str_values = set([force_unicode(v) for v in value]) |
256 | 289 | for option_value, option_label in chain(self.choices, choices): |
257 | 290 | option_value = force_unicode(option_value) |
258 | | selected_html = (option_value in str_values) and ' selected="selected"' or '' |
| 291 | selected_html = (option_value in str_values) and \ |
| 292 | ' selected="selected"' or '' |
259 | 293 | output.append(u'<option value="%s"%s>%s</option>' % ( |
260 | 294 | escape(option_value), selected_html, |
261 | 295 | conditional_escape(force_unicode(option_label)))) |
… |
… |
class SelectMultiple(Widget):
|
267 | 301 | return data.getlist(name) |
268 | 302 | return data.get(name, None) |
269 | 303 | |
| 304 | |
270 | 305 | class RadioInput(StrAndUnicode): |
271 | 306 | """ |
272 | 307 | An object used by RadioFieldRenderer that represents a single |
… |
… |
class RadioInput(StrAndUnicode):
|
286 | 321 | else: |
287 | 322 | label_for = '' |
288 | 323 | choice_label = conditional_escape(force_unicode(self.choice_label)) |
289 | | return mark_safe(u'<label%s>%s %s</label>' % (label_for, self.tag(), choice_label)) |
| 324 | return mark_safe(u'<label%s>%s %s</label>' % ( |
| 325 | label_for, self.tag(), choice_label)) |
290 | 326 | |
291 | 327 | def is_checked(self): |
292 | 328 | return self.value == self.choice_value |
… |
… |
class RadioInput(StrAndUnicode):
|
294 | 330 | def tag(self): |
295 | 331 | if 'id' in self.attrs: |
296 | 332 | self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index) |
297 | | final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value) |
| 333 | final_attrs = dict(self.attrs, type='radio', |
| 334 | name=self.name, value=self.choice_value) |
298 | 335 | if self.is_checked(): |
299 | 336 | final_attrs['checked'] = 'checked' |
300 | 337 | return mark_safe(u'<input%s />' % flatatt(final_attrs)) |
301 | 338 | |
| 339 | |
302 | 340 | class RadioFieldRenderer(StrAndUnicode): |
303 | 341 | """ |
304 | 342 | An object used by RadioSelect to enable customization of radio widgets. |
… |
… |
class RadioFieldRenderer(StrAndUnicode):
|
310 | 348 | |
311 | 349 | def __iter__(self): |
312 | 350 | for i, choice in enumerate(self.choices): |
313 | | yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i) |
| 351 | yield RadioInput(self.name, self.value, self.attrs.copy(), |
| 352 | choice, i) |
314 | 353 | |
315 | 354 | def __getitem__(self, idx): |
316 | 355 | choice = self.choices[idx] # Let the IndexError propogate |
317 | | return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx) |
| 356 | return RadioInput(self.name, self.value, self.attrs.copy(), |
| 357 | choice, idx) |
318 | 358 | |
319 | 359 | def __unicode__(self): |
320 | 360 | return self.render() |
… |
… |
class RadioFieldRenderer(StrAndUnicode):
|
324 | 364 | return mark_safe(u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' |
325 | 365 | % force_unicode(w) for w in self])) |
326 | 366 | |
| 367 | |
327 | 368 | class RadioSelect(Select): |
328 | 369 | renderer = RadioFieldRenderer |
329 | 370 | |
… |
… |
class RadioSelect(Select):
|
336 | 377 | |
337 | 378 | def get_renderer(self, name, value, attrs=None, choices=()): |
338 | 379 | """Returns an instance of the renderer.""" |
339 | | if value is None: value = '' |
| 380 | if value is None: |
| 381 | value = '' |
340 | 382 | str_value = force_unicode(value) # Normalize to string. |
341 | 383 | final_attrs = self.build_attrs(attrs) |
342 | 384 | choices = list(chain(self.choices, choices)) |
… |
… |
class RadioSelect(Select):
|
355 | 397 | return id_ |
356 | 398 | id_for_label = classmethod(id_for_label) |
357 | 399 | |
| 400 | |
358 | 401 | class CheckboxSelectMultiple(SelectMultiple): |
| 402 | |
359 | 403 | def render(self, name, value, attrs=None, choices=()): |
360 | | if value is None: value = [] |
| 404 | if value is None: |
| 405 | value = [] |
361 | 406 | has_id = attrs and 'id' in attrs |
362 | 407 | final_attrs = self.build_attrs(attrs, name=name) |
363 | 408 | output = [u'<ul>'] |
364 | 409 | # Normalize to strings |
365 | 410 | str_values = set([force_unicode(v) for v in value]) |
366 | | for i, (option_value, option_label) in enumerate(chain(self.choices, choices)): |
| 411 | for i, (option_value, option_label) in enumerate(chain( |
| 412 | self.choices, choices)): |
367 | 413 | # If an ID attribute was given, add a numeric index as a suffix, |
368 | 414 | # so that the checkboxes don't all have the same ID attribute. |
369 | 415 | if has_id: |
… |
… |
class CheckboxSelectMultiple(SelectMultiple):
|
371 | 417 | label_for = u' for="%s"' % final_attrs['id'] |
372 | 418 | else: |
373 | 419 | label_for = '' |
374 | | |
375 | | cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values) |
| 420 | |
| 421 | cb = CheckboxInput(final_attrs, |
| 422 | check_test=lambda value: value in str_values) |
376 | 423 | option_value = force_unicode(option_value) |
377 | 424 | rendered_cb = cb.render(name, option_value) |
378 | 425 | option_label = conditional_escape(force_unicode(option_label)) |
379 | | output.append(u'<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label)) |
| 426 | output.append(u'<li><label%s>%s %s</label></li>' % ( |
| 427 | label_for, rendered_cb, option_label)) |
380 | 428 | output.append(u'</ul>') |
381 | 429 | return mark_safe(u'\n'.join(output)) |
382 | 430 | |
… |
… |
class CheckboxSelectMultiple(SelectMultiple):
|
387 | 435 | return id_ |
388 | 436 | id_for_label = classmethod(id_for_label) |
389 | 437 | |
| 438 | |
390 | 439 | class MultiWidget(Widget): |
391 | 440 | """ |
392 | 441 | A widget that is composed of multiple widgets. |
… |
… |
class MultiWidget(Widget):
|
414 | 463 | |
415 | 464 | You'll probably want to use this class with MultiValueField. |
416 | 465 | """ |
| 466 | |
417 | 467 | def __init__(self, widgets, attrs=None): |
418 | 468 | self.widgets = [isinstance(w, type) and w() or w for w in widgets] |
419 | 469 | super(MultiWidget, self).__init__(attrs) |
… |
… |
class MultiWidget(Widget):
|
433 | 483 | widget_value = None |
434 | 484 | if id_: |
435 | 485 | final_attrs = dict(final_attrs, id='%s_%s' % (id_, i)) |
436 | | output.append(widget.render(name + '_%s' % i, widget_value, final_attrs)) |
| 486 | output.append(widget.render(name + '_%s' % i, widget_value, |
| 487 | final_attrs)) |
437 | 488 | return mark_safe(self.format_output(output)) |
438 | 489 | |
439 | 490 | def id_for_label(self, id_): |
… |
… |
class MultiWidget(Widget):
|
444 | 495 | id_for_label = classmethod(id_for_label) |
445 | 496 | |
446 | 497 | def value_from_datadict(self, data, files, name): |
447 | | return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)] |
| 498 | return [widget.value_from_datadict(data, files, name + '_%s' % i) \ |
| 499 | for i, widget in enumerate(self.widgets)] |
448 | 500 | |
449 | 501 | def format_output(self, rendered_widgets): |
450 | 502 | """ |
… |
… |
class MultiWidget(Widget):
|
464 | 516 | """ |
465 | 517 | raise NotImplementedError('Subclasses must implement this method.') |
466 | 518 | |
| 519 | |
467 | 520 | class SplitDateTimeWidget(MultiWidget): |
468 | 521 | """ |
469 | 522 | A Widget that splits datetime input into two <input type="text"> boxes. |
470 | 523 | """ |
| 524 | |
471 | 525 | def __init__(self, attrs=None): |
472 | 526 | widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs)) |
473 | 527 | super(SplitDateTimeWidget, self).__init__(widgets, attrs) |
… |
… |
class SplitDateTimeWidget(MultiWidget):
|
476 | 530 | if value: |
477 | 531 | return [value.date(), value.time().replace(microsecond=0)] |
478 | 532 | return [None, None] |
479 | | |