Django

Code

root/django/trunk/tests/regressiontests/forms/widgets.py

Revision 8816, 49.6 kB (checked in by brosner, 6 days ago)

Fixed #7975 -- Callable defaults in inline model formsets now work correctly. Based on patch from msaelices. Thanks for your hard work msaelices.

  • Property svn:eol-style set to native
Line 
1 # -*- coding: utf-8 -*-
2 tests = r"""
3 >>> from django.forms import *
4 >>> from django.forms.widgets import RadioFieldRenderer
5 >>> from django.utils.safestring import mark_safe
6 >>> import datetime
7 >>> import time
8 >>> import re
9 >>> try:
10 ...     from decimal import Decimal
11 ... except ImportError:
12 ...     from django.utils._decimal import Decimal
13
14 ###########
15 # Widgets #
16 ###########
17
18 Each Widget class corresponds to an HTML form widget. A Widget knows how to
19 render itself, given a field name and some data. Widgets don't perform
20 validation.
21
22 # TextInput Widget ############################################################
23
24 >>> w = TextInput()
25 >>> w.render('email', '')
26 u'<input type="text" name="email" />'
27 >>> w.render('email', None)
28 u'<input type="text" name="email" />'
29 >>> w.render('email', 'test@example.com')
30 u'<input type="text" name="email" value="test@example.com" />'
31 >>> w.render('email', 'some "quoted" & ampersanded value')
32 u'<input type="text" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
33 >>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
34 u'<input type="text" name="email" value="test@example.com" class="fun" />'
35
36 # Note that doctest in Python 2.4 (and maybe 2.5?) doesn't support non-ascii
37 # characters in output, so we're displaying the repr() here.
38 >>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
39 u'<input type="text" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" class="fun" />'
40
41 You can also pass 'attrs' to the constructor:
42 >>> w = TextInput(attrs={'class': 'fun'})
43 >>> w.render('email', '')
44 u'<input type="text" class="fun" name="email" />'
45 >>> w.render('email', 'foo@example.com')
46 u'<input type="text" class="fun" value="foo@example.com" name="email" />'
47
48 'attrs' passed to render() get precedence over those passed to the constructor:
49 >>> w = TextInput(attrs={'class': 'pretty'})
50 >>> w.render('email', '', attrs={'class': 'special'})
51 u'<input type="text" class="special" name="email" />'
52
53 'attrs' can be safe-strings if needed
54 >>> w = TextInput(attrs={'onBlur': mark_safe("function('foo')")})
55 >>> print w.render('email', '')
56 <input onBlur="function('foo')" type="text" name="email" />
57
58 # PasswordInput Widget ############################################################
59
60 >>> w = PasswordInput()
61 >>> w.render('email', '')
62 u'<input type="password" name="email" />'
63 >>> w.render('email', None)
64 u'<input type="password" name="email" />'
65 >>> w.render('email', 'test@example.com')
66 u'<input type="password" name="email" value="test@example.com" />'
67 >>> w.render('email', 'some "quoted" & ampersanded value')
68 u'<input type="password" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
69 >>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
70 u'<input type="password" name="email" value="test@example.com" class="fun" />'
71
72 You can also pass 'attrs' to the constructor:
73 >>> w = PasswordInput(attrs={'class': 'fun'})
74 >>> w.render('email', '')
75 u'<input type="password" class="fun" name="email" />'
76 >>> w.render('email', 'foo@example.com')
77 u'<input type="password" class="fun" value="foo@example.com" name="email" />'
78
79 'attrs' passed to render() get precedence over those passed to the constructor:
80 >>> w = PasswordInput(attrs={'class': 'pretty'})
81 >>> w.render('email', '', attrs={'class': 'special'})
82 u'<input type="password" class="special" name="email" />'
83
84 >>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
85 u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
86
87 The render_value argument lets you specify whether the widget should render
88 its value. You may want to do this for security reasons.
89 >>> w = PasswordInput(render_value=True)
90 >>> w.render('email', 'secret')
91 u'<input type="password" name="email" value="secret" />'
92 >>> w = PasswordInput(render_value=False)
93 >>> w.render('email', '')
94 u'<input type="password" name="email" />'
95 >>> w.render('email', None)
96 u'<input type="password" name="email" />'
97 >>> w.render('email', 'secret')
98 u'<input type="password" name="email" />'
99 >>> w = PasswordInput(attrs={'class': 'fun'}, render_value=False)
100 >>> w.render('email', 'secret')
101 u'<input type="password" class="fun" name="email" />'
102
103 # HiddenInput Widget ############################################################
104
105 >>> w = HiddenInput()
106 >>> w.render('email', '')
107 u'<input type="hidden" name="email" />'
108 >>> w.render('email', None)
109 u'<input type="hidden" name="email" />'
110 >>> w.render('email', 'test@example.com')
111 u'<input type="hidden" name="email" value="test@example.com" />'
112 >>> w.render('email', 'some "quoted" & ampersanded value')
113 u'<input type="hidden" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
114 >>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
115 u'<input type="hidden" name="email" value="test@example.com" class="fun" />'
116
117 You can also pass 'attrs' to the constructor:
118 >>> w = HiddenInput(attrs={'class': 'fun'})
119 >>> w.render('email', '')
120 u'<input type="hidden" class="fun" name="email" />'
121 >>> w.render('email', 'foo@example.com')
122 u'<input type="hidden" class="fun" value="foo@example.com" name="email" />'
123
124 'attrs' passed to render() get precedence over those passed to the constructor:
125 >>> w = HiddenInput(attrs={'class': 'pretty'})
126 >>> w.render('email', '', attrs={'class': 'special'})
127 u'<input type="hidden" class="special" name="email" />'
128
129 >>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
130 u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
131
132 'attrs' passed to render() get precedence over those passed to the constructor:
133 >>> w = HiddenInput(attrs={'class': 'pretty'})
134 >>> w.render('email', '', attrs={'class': 'special'})
135 u'<input type="hidden" class="special" name="email" />'
136
137 Boolean values are rendered to their string forms ("True" and "False").
138 >>> w = HiddenInput()
139 >>> w.render('get_spam', False)
140 u'<input type="hidden" name="get_spam" value="False" />'
141 >>> w.render('get_spam', True)
142 u'<input type="hidden" name="get_spam" value="True" />'
143
144 # MultipleHiddenInput Widget ##################################################
145
146 >>> w = MultipleHiddenInput()
147 >>> w.render('email', [])
148 u''
149 >>> w.render('email', None)
150 u''
151 >>> w.render('email', ['test@example.com'])
152 u'<input type="hidden" name="email" value="test@example.com" />'
153 >>> w.render('email', ['some "quoted" & ampersanded value'])
154 u'<input type="hidden" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
155 >>> w.render('email', ['test@example.com', 'foo@example.com'])
156 u'<input type="hidden" name="email" value="test@example.com" />\n<input type="hidden" name="email" value="foo@example.com" />'
157 >>> w.render('email', ['test@example.com'], attrs={'class': 'fun'})
158 u'<input type="hidden" name="email" value="test@example.com" class="fun" />'
159 >>> w.render('email', ['test@example.com', 'foo@example.com'], attrs={'class': 'fun'})
160 u'<input type="hidden" name="email" value="test@example.com" class="fun" />\n<input type="hidden" name="email" value="foo@example.com" class="fun" />'
161
162 You can also pass 'attrs' to the constructor:
163 >>> w = MultipleHiddenInput(attrs={'class': 'fun'})
164 >>> w.render('email', [])
165 u''
166 >>> w.render('email', ['foo@example.com'])
167 u'<input type="hidden" class="fun" value="foo@example.com" name="email" />'
168 >>> w.render('email', ['foo@example.com', 'test@example.com'])
169 u'<input type="hidden" class="fun" value="foo@example.com" name="email" />\n<input type="hidden" class="fun" value="test@example.com" name="email" />'
170
171 'attrs' passed to render() get precedence over those passed to the constructor:
172 >>> w = MultipleHiddenInput(attrs={'class': 'pretty'})
173 >>> w.render('email', ['foo@example.com'], attrs={'class': 'special'})
174 u'<input type="hidden" class="special" value="foo@example.com" name="email" />'
175
176 >>> w.render('email', ['ŠĐĆŽćžšđ'], attrs={'class': 'fun'})
177 u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
178
179 'attrs' passed to render() get precedence over those passed to the constructor:
180 >>> w = MultipleHiddenInput(attrs={'class': 'pretty'})
181 >>> w.render('email', ['foo@example.com'], attrs={'class': 'special'})
182 u'<input type="hidden" class="special" value="foo@example.com" name="email" />'
183
184 # FileInput Widget ############################################################
185
186 FileInput widgets don't ever show the value, because the old value is of no use
187 if you are updating the form or if the provided file generated an error.
188 >>> w = FileInput()
189 >>> w.render('email', '')
190 u'<input type="file" name="email" />'
191 >>> w.render('email', None)
192 u'<input type="file" name="email" />'
193 >>> w.render('email', 'test@example.com')
194 u'<input type="file" name="email" />'
195 >>> w.render('email', 'some "quoted" & ampersanded value')
196 u'<input type="file" name="email" />'
197 >>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
198 u'<input type="file" name="email" class="fun" />'
199
200 You can also pass 'attrs' to the constructor:
201 >>> w = FileInput(attrs={'class': 'fun'})
202 >>> w.render('email', '')
203 u'<input type="file" class="fun" name="email" />'
204 >>> w.render('email', 'foo@example.com')
205 u'<input type="file" class="fun" name="email" />'
206
207 >>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
208 u'<input type="file" class="fun" name="email" />'
209
210 Test for the behavior of _has_changed for FileInput. The value of data will
211 more than likely come from request.FILES. The value of initial data will
212 likely be a filename stored in the database. Since its value is of no use to
213 a FileInput it is ignored.
214
215 >>> w = FileInput()
216
217 # No file was uploaded and no initial data.
218 >>> w._has_changed(u'', None)
219 False
220
221 # A file was uploaded and no initial data.
222 >>> w._has_changed(u'', {'filename': 'resume.txt', 'content': 'My resume'})
223 True
224
225 # A file was not uploaded, but there is initial data
226 >>> w._has_changed(u'resume.txt', None)
227 False
228
229 # A file was uploaded and there is initial data (file identity is not dealt
230 # with here)
231 >>> w._has_changed('resume.txt', {'filename': 'resume.txt', 'content': 'My resume'})
232 True
233
234 # Textarea Widget #############################################################
235
236 >>> w = Textarea()
237 >>> w.render('msg', '')
238 u'<textarea rows="10" cols="40" name="msg"></textarea>'
239 >>> w.render('msg', None)
240 u'<textarea rows="10" cols="40" name="msg"></textarea>'
241 >>> w.render('msg', 'value')
242 u'<textarea rows="10" cols="40" name="msg">value</textarea>'
243 >>> w.render('msg', 'some "quoted" & ampersanded value')
244 u'<textarea rows="10" cols="40" name="msg">some &quot;quoted&quot; &amp; ampersanded value</textarea>'
245 >>> w.render('msg', mark_safe('pre &quot;quoted&quot; value'))
246 u'<textarea rows="10" cols="40" name="msg">pre &quot;quoted&quot; value</textarea>'
247 >>> w.render('msg', 'value', attrs={'class': 'pretty', 'rows': 20})
248 u'<textarea class="pretty" rows="20" cols="40" name="msg">value</textarea>'
249
250 You can also pass 'attrs' to the constructor:
251 >>> w = Textarea(attrs={'class': 'pretty'})
252 >>> w.render('msg', '')
253 u'<textarea rows="10" cols="40" name="msg" class="pretty"></textarea>'
254 >>> w.render('msg', 'example')
255 u'<textarea rows="10" cols="40" name="msg" class="pretty">example</textarea>'
256
257 'attrs' passed to render() get precedence over those passed to the constructor:
258 >>> w = Textarea(attrs={'class': 'pretty'})
259 >>> w.render('msg', '', attrs={'class': 'special'})
260 u'<textarea rows="10" cols="40" name="msg" class="special"></textarea>'
261
262 >>> w.render('msg', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
263 u'<textarea rows="10" cols="40" name="msg" class="fun">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</textarea>'
264
265 # CheckboxInput Widget ########################################################
266
267 >>> w = CheckboxInput()
268 >>> w.render('is_cool', '')
269 u'<input type="checkbox" name="is_cool" />'
270 >>> w.render('is_cool', None)
271 u'<input type="checkbox" name="is_cool" />'
272 >>> w.render('is_cool', False)
273 u'<input type="checkbox" name="is_cool" />'
274 >>> w.render('is_cool', True)
275 u'<input checked="checked" type="checkbox" name="is_cool" />'
276
277 Using any value that's not in ('', None, False, True) will check the checkbox
278 and set the 'value' attribute.
279 >>> w.render('is_cool', 'foo')
280 u'<input checked="checked" type="checkbox" name="is_cool" value="foo" />'
281
282 >>> w.render('is_cool', False, attrs={'class': 'pretty'})
283 u'<input type="checkbox" name="is_cool" class="pretty" />'
284
285 You can also pass 'attrs' to the constructor:
286 >>> w = CheckboxInput(attrs={'class': 'pretty'})
287 >>> w.render('is_cool', '')
288 u'<input type="checkbox" class="pretty" name="is_cool" />'
289
290 'attrs' passed to render() get precedence over those passed to the constructor:
291 >>> w = CheckboxInput(attrs={'class': 'pretty'})
292 >>> w.render('is_cool', '', attrs={'class': 'special'})
293 u'<input type="checkbox" class="special" name="is_cool" />'
294
295 You can pass 'check_test' to the constructor. This is a callable that takes the
296 value and returns True if the box should be checked.
297 >>> w = CheckboxInput(check_test=lambda value: value.startswith('hello'))
298 >>> w.render('greeting', '')
299 u'<input type="checkbox" name="greeting" />'
300 >>> w.render('greeting', 'hello')
301 u'<input checked="checked" type="checkbox" name="greeting" value="hello" />'
302 >>> w.render('greeting', 'hello there')
303 u'<input checked="checked" type="checkbox" name="greeting" value="hello there" />'
304 >>> w.render('greeting', 'hello & goodbye')
305 u'<input checked="checked" type="checkbox" name="greeting" value="hello &amp; goodbye" />'
306
307 A subtlety: If the 'check_test' argument cannot handle a value and raises any
308 exception during its __call__, then the exception will be swallowed and the box
309 will not be checked. In this example, the 'check_test' assumes the value has a
310 startswith() method, which fails for the values True, False and None.
311 >>> w.render('greeting', True)
312 u'<input type="checkbox" name="greeting" />'
313 >>> w.render('greeting', False)
314 u'<input type="checkbox" name="greeting" />'
315 >>> w.render('greeting', None)
316 u'<input type="checkbox" name="greeting" />'
317
318 The CheckboxInput widget will return False if the key is not found in the data
319 dictionary (because HTML form submission doesn't send any result for unchecked
320 checkboxes).
321 >>> w.value_from_datadict({}, {}, 'testing')
322 False
323
324 >>> w._has_changed(None, None)
325 False
326 >>> w._has_changed(None, u'')
327 False
328 >>> w._has_changed(u'', None)
329 False
330 >>> w._has_changed(u'', u'')
331 False
332 >>> w._has_changed(False, u'on')
333 True
334 >>> w._has_changed(True, u'on')
335 False
336 >>> w._has_changed(True, u'')
337 True
338
339 # Select Widget ###############################################################
340
341 >>> w = Select()
342 >>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
343 <select name="beatle">
344 <option value="J" selected="selected">John</option>
345 <option value="P">Paul</option>
346 <option value="G">George</option>
347 <option value="R">Ringo</option>
348 </select>
349
350 If the value is None, none of the options are selected:
351 >>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
352 <select name="beatle">
353 <option value="J">John</option>
354 <option value="P">Paul</option>
355 <option value="G">George</option>
356 <option value="R">Ringo</option>
357 </select>
358
359 If the value corresponds to a label (but not to an option value), none of the options are selected:
360 >>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
361 <select name="beatle">
362 <option value="J">John</option>
363 <option value="P">Paul</option>
364 <option value="G">George</option>
365 <option value="R">Ringo</option>
366 </select>
367
368 The value is compared to its str():
369 >>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')])
370 <select name="num">
371 <option value="1">1</option>
372 <option value="2" selected="selected">2</option>
373 <option value="3">3</option>
374 </select>
375 >>> print w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)])
376 <select name="num">
377 <option value="1">1</option>
378 <option value="2" selected="selected">2</option>
379 <option value="3">3</option>
380 </select>
381 >>> print w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)])
382 <select name="num">
383 <option value="1">1</option>
384 <option value="2" selected="selected">2</option>
385 <option value="3">3</option>
386 </select>
387
388 The 'choices' argument can be any iterable:
389 >>> from itertools import chain
390 >>> def get_choices():
391 ...     for i in range(5):
392 ...         yield (i, i)
393 >>> print w.render('num', 2, choices=get_choices())
394 <select name="num">
395 <option value="0">0</option>
396 <option value="1">1</option>
397 <option value="2" selected="selected">2</option>
398 <option value="3">3</option>
399 <option value="4">4</option>
400 </select>
401 >>> things = ({'id': 1, 'name': 'And Boom'}, {'id': 2, 'name': 'One More Thing!'})
402 >>> class SomeForm(Form):
403 ...     somechoice = ChoiceField(choices=chain((('', '-'*9),), [(thing['id'], thing['name']) for thing in things]))
404 >>> f = SomeForm()
405 >>> f.as_table()
406 u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>'
407 >>> f.as_table()
408 u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>'
409 >>> f = SomeForm({'somechoice': 2})
410 >>> f.as_table()
411 u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="">---------</option>\n<option value="1">And Boom</option>\n<option value="2" selected="selected">One More Thing!</option>\n</select></td></tr>'
412
413 You can also pass 'choices' to the constructor:
414 >>> w = Select(choices=[(1, 1), (2, 2), (3, 3)])
415 >>> print w.render('num', 2)
416 <select name="num">
417 <option value="1">1</option>
418 <option value="2" selected="selected">2</option>
419 <option value="3">3</option>
420 </select>
421
422 If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
423 >>> print w.render('num', 2, choices=[(4, 4), (5, 5)])
424 <select name="num">
425 <option value="1">1</option>
426 <option value="2" selected="selected">2</option>
427 <option value="3">3</option>
428 <option value="4">4</option>
429 <option value="5">5</option>
430 </select>
431
432 # Choices are escaped correctly
433 >>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you &gt; me'))))
434 <select name="escape">
435 <option value="1">1</option>
436 <option value="2">2</option>
437 <option value="3">3</option>
438 <option value="bad">you &amp; me</option>
439 <option value="good">you &gt; me</option>
440 </select>
441
442 # Unicode choices are correctly rendered as HTML
443 >>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
444 u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
445
446 If choices is passed to the constructor and is a generator, it can be iterated
447 over multiple times without getting consumed:
448 >>> w = Select(choices=get_choices())
449 >>> print w.render('num', 2)
450 <select name="num">
451 <option value="0">0</option>
452 <option value="1">1</option>
453 <option value="2" selected="selected">2</option>
454 <option value="3">3</option>
455 <option value="4">4</option>
456 </select>
457 >>> print w.render('num', 3)
458 <select name="num">
459 <option value="0">0</option>
460 <option value="1">1</option>
461 <option value="2">2</option>
462 <option value="3" selected="selected">3</option>
463 <option value="4">4</option>
464 </select>
465
466 Choices can be nested one level in order to create HTML optgroups:
467 >>> w.choices=(('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
468 >>> print w.render('nestchoice', None)
469 <select name="nestchoice">
470 <option value="outer1">Outer 1</option>
471 <optgroup label="Group &quot;1&quot;">
472 <option value="inner1">Inner 1</option>
473 <option value="inner2">Inner 2</option>
474 </optgroup>
475 </select>
476
477 >>> print w.render('nestchoice', 'outer1')
478 <select name="nestchoice">
479 <option value="outer1" selected="selected">Outer 1</option>
480 <optgroup label="Group &quot;1&quot;">
481 <option value="inner1">Inner 1</option>
482 <option value="inner2">Inner 2</option>
483 </optgroup>
484 </select>
485
486 >>> print w.render('nestchoice', 'inner1')
487 <select name="nestchoice">
488 <option value="outer1">Outer 1</option>
489 <optgroup label="Group &quot;1&quot;">
490 <option value="inner1" selected="selected">Inner 1</option>
491 <option value="inner2">Inner 2</option>
492 </optgroup>
493 </select>
494
495 # NullBooleanSelect Widget ####################################################
496
497 >>> w = NullBooleanSelect()
498 >>> print w.render('is_cool', True)
499 <select name="is_cool">
500 <option value="1">Unknown</option>
501 <option value="2" selected="selected">Yes</option>
502 <option value="3">No</option>
503 </select>
504 >>> print w.render('is_cool', False)
505 <select name="is_cool">
506 <option value="1">Unknown</option>
507 <option value="2">Yes</option>
508 <option value="3" selected="selected">No</option>
509 </select>
510 >>> print w.render('is_cool', None)
511 <select name="is_cool">
512 <option value="1" selected="selected">Unknown</option>
513 <option value="2">Yes</option>
514 <option value="3">No</option>
515 </select>
516 >>> print w.render('is_cool', '2')
517 <select name="is_cool">
518 <option value="1">Unknown</option>
519 <option value="2" selected="selected">Yes</option>
520 <option value="3">No</option>
521 </select>
522 >>> print w.render('is_cool', '3')
523 <select name="is_cool">
524 <option value="1">Unknown</option>
525 <option value="2">Yes</option>
526 <option value="3" selected="selected">No</option>
527 </select>
528
529 """ + \
530 r""" # [This concatenation is to keep the string below the jython's 32K limit].
531 # SelectMultiple Widget #######################################################
532
533 >>> w = SelectMultiple()
534 >>> print w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
535 <select multiple="multiple" name="beatles">
536 <option value="J" selected="selected">John</option>
537 <option value="P">Paul</option>
538 <option value="G">George</option>
539 <option value="R">Ringo</option>
540 </select>
541 >>> print w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
542 <select multiple="multiple" name="beatles">
543 <option value="J" selected="selected">John</option>
544 <option value="P" selected="selected">Paul</option>
545 <option value="G">George</option>
546 <option value="R">Ringo</option>
547 </select>
548 >>> print w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
549 <select multiple="multiple" name="beatles">
550 <option value="J" selected="selected">John</option>
551 <option value="P" selected="selected">Paul</option>
552 <option value="G">George</option>
553 <option value="R" selected="selected">Ringo</option>
554 </select>
555
556 If the value is None, none of the options are selected:
557 >>> print w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
558 <select multiple="multiple" name="beatles">
559 <option value="J">John</option>
560 <option value="P">Paul</option>
561 <option value="G">George</option>
562 <option value="R">Ringo</option>
563 </select>
564
565 If the value corresponds to a label (but not to an option value), none of the options are selected:
566 >>> print w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
567 <select multiple="multiple" name="beatles">
568 <option value="J">John</option>
569 <option value="P">Paul</option>
570 <option value="G">George</option>
571 <option value="R">Ringo</option>
572 </select>
573
574 If multiple values are given, but some of them are not valid, the valid ones are selected:
575 >>> print w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
576 <select multiple="multiple" name="beatles">
577 <option value="J" selected="selected">John</option>
578 <option value="P">Paul</option>
579 <option value="G" selected="selected">George</option>
580 <option value="R">Ringo</option>
581 </select>
582
583 The value is compared to its str():
584 >>> print w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')])
585 <select multiple="multiple" name="nums">
586 <option value="1">1</option>
587 <option value="2" selected="selected">2</option>
588 <option value="3">3</option>
589 </select>
590 >>> print w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)])
591 <select multiple="multiple" name="nums">
592 <option value="1">1</option>
593 <option value="2" selected="selected">2</option>
594 <option value="3">3</option>
595 </select>
596 >>> print w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)])
597 <select multiple="multiple" name="nums">
598 <option value="1">1</option>
599 <option value="2" selected="selected">2</option>
600 <option value="3">3</option>
601 </select>
602
603 The 'choices' argument can be any iterable:
604 >>> def get_choices():
605 ...     for i in range(5):
606 ...         yield (i, i)
607 >>> print w.render('nums', [2], choices=get_choices())
608 <select multiple="multiple" name="nums">
609 <option value="0">0</option>
610 <option value="1">1</option>
611 <option value="2" selected="selected">2</option>
612 <option value="3">3</option>
613 <option value="4">4</option>
614 </select>
615
616 You can also pass 'choices' to the constructor:
617 >>> w = SelectMultiple(choices=[(1, 1), (2, 2), (3, 3)])
618 >>> print w.render('nums', [2])
619 <select multiple="multiple" name="nums">
620 <option value="1">1</option>
621 <option value="2" selected="selected">2</option>
622 <option value="3">3</option>
623 </select>
624
625 If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
626 >>> print w.render('nums', [2], choices=[(4, 4), (5, 5)])
627 <select multiple="multiple" name="nums">
628 <option value="1">1</option>
629 <option value="2" selected="selected">2</option>
630 <option value="3">3</option>
631 <option value="4">4</option>
632 <option value="5">5</option>
633 </select>
634
635 # Choices are escaped correctly
636 >>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you &gt; me'))))
637 <select multiple="multiple" name="escape">
638 <option value="1">1</option>
639 <option value="2">2</option>
640 <option value="3">3</option>
641 <option value="bad">you &amp; me</option>
642 <option value="good">you &gt; me</option>
643 </select>
644
645 # Unicode choices are correctly rendered as HTML
646 >>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
647 u'<select multiple="multiple" name="nums">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
648
649 # Test the usage of _has_changed
650 >>> w._has_changed(None, None)
651 False
652 >>> w._has_changed([], None)
653 False
654 >>> w._has_changed(None, [u'1'])
655 True
656 >>> w._has_changed([1, 2], [u'1', u'2'])
657 False
658 >>> w._has_changed([1, 2], [u'1'])
659 True
660 >>> w._has_changed([1, 2], [u'1', u'3'])
661 True
662
663 # Choices can be nested one level in order to create HTML optgroups:
664 >>> w.choices = (('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
665 >>> print w.render('nestchoice', None)
666 <select multiple="multiple" name="nestchoice">
667 <option value="outer1">Outer 1</option>
668 <optgroup label="Group &quot;1&quot;">
669 <option value="inner1">Inner 1</option>
670 <option value="inner2">Inner 2</option>
671 </optgroup>
672 </select>
673
674 >>> print w.render('nestchoice', ['outer1'])
675 <select multiple="multiple" name="nestchoice">
676 <option value="outer1" selected="selected">Outer 1</option>
677 <optgroup label="Group &quot;1&quot;">
678 <option value="inner1">Inner 1</option>
679 <option value="inner2">Inner 2</option>
680 </optgroup>
681 </select>
682
683 >>> print w.render('nestchoice', ['inner1'])
684 <select multiple="multiple" name="nestchoice">
685 <option value="outer1">Outer 1</option>
686 <optgroup label="Group &quot;1&quot;">
687 <option value="inner1" selected="selected">Inner 1</option>
688 <option value="inner2">Inner 2</option>
689 </optgroup>
690 </select>
691
692 >>> print w.render('nestchoice', ['outer1', 'inner2'])
693 <select multiple="multiple" name="nestchoice">
694 <option value="outer1" selected="selected">Outer 1</option>
695 <optgroup label="Group &quot;1&quot;">
696 <option value="inner1">Inner 1</option>
697 <option value="inner2" selected="selected">Inner 2</option>
698 </optgroup>
699 </select>
700
701 # RadioSelect Widget ##########################################################
702
703 >>> w = RadioSelect()
704 >>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
705 <ul>
706 <li><label><input checked="checked" type="radio" name="beatle" value="J" /> John</label></li>
707 <li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
708 <li><label><input type="radio" name="beatle" value="G" /> George</label></li>
709 <li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
710 </ul>
711
712 If the value is None, none of the options are checked:
713 >>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
714 <ul>
715 <li><label><input type="radio" name="beatle" value="J" /> John</label></li>
716 <li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
717 <li><label><input type="radio" name="beatle" value="G" /> George</label></li>
718 <li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
719 </ul>
720
721 If the value corresponds to a label (but not to an option value), none of the options are checked:
722 >>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
723 <ul>
724 <li><label><input type="radio" name="beatle" value="J" /> John</label></li>
725 <li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
726 <li><label><input type="radio" name="beatle" value="G" /> George</label></li>
727 <li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
728 </ul>
729
730 The value is compared to its str():
731 >>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')])
732 <ul>
733 <li><label><input type="radio" name="num" value="1" /> 1</label></li>
734 <li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
735 <li><label><input type="radio" name="num" value="3" /> 3</label></li>
736 </ul>
737 >>> print w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)])
738 <ul>
739 <li><label><input type="radio" name="num" value="1" /> 1</label></li>
740 <li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
741 <li><label><input type="radio" name="num" value="3" /> 3</label></li>
742 </ul>
743 >>> print w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)])
744 <ul>
745 <li><label><input type="radio" name="num" value="1" /> 1</label></li>
746 <li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
747 <li><label><input type="radio" name="num" value="3" /> 3</label></li>
748 </ul>
749
750 The 'choices' argument can be any iterable:
751 >>> def get_choices():
752 ...     for i in range(5):
753 ...         yield (i, i)
754 >>> print w.render('num', 2, choices=get_choices())
755 <ul>
756 <li><label><input type="radio" name="num" value="0" /> 0</label></li>
757 <li><label><input type="radio" name="num" value="1" /> 1</label></li>
758 <li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
759 <li><label><input type="radio" name="num" value="3" /> 3</label></li>
760 <li><label><input type="radio" name="num" value="4" /> 4</label></li>
761 </ul>
762
763 You can also pass 'choices' to the constructor:
764 >>> w = RadioSelect(choices=[(1, 1), (2, 2), (3, 3)])
765 >>> print w.render('num', 2)
766 <ul>
767 <li><label><input type="radio" name="num" value="1" /> 1</label></li>
768 <li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
769 <li><label><input type="radio" name="num" value="3" /> 3</label></li>
770 </ul>
771
772 If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
773 >>> print w.render('num', 2, choices=[(4, 4), (5, 5)])
774 <ul>
775 <li><label><input type="radio" name="num" value="1" /> 1</label></li>
776 <li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
777 <li><label><input type="radio" name="num" value="3" /> 3</label></li>
778 <li><label><input type="radio" name="num" value="4" /> 4</label></li>
779 <li><label><input type="radio" name="num" value="5" /> 5</label></li>
780 </ul>
781
782 RadioSelect uses a RadioFieldRenderer to render the individual radio inputs.
783 You can manipulate that object directly to customize the way the RadioSelect
784 is rendered.
785 >>> w = RadioSelect()
786 >>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
787 >>> for inp in r:
788 ...     print inp
789 <label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
790 <label><input type="radio" name="beatle" value="P" /> Paul</label>
791 <label><input type="radio" name="beatle" value="G" /> George</label>
792 <label><input type="radio" name="beatle" value="R" /> Ringo</label>
793 >>> for inp in r:
794 ...     print '%s<br />' % inp
795 <label><input checked="checked" type="radio" name="beatle" value="J" /> John</label><br />
796 <label><input type="radio" name="beatle" value="P" /> Paul</label><br />
797 <label><input type="radio" name="beatle" value="G" /> George</label><br />
798 <label><input type="radio" name="beatle" value="R" /> Ringo</label><br />
799 >>> for inp in r:
800 ... print '<p>%s %s</p>' % (inp.tag(), inp.choice_label)
801 <p><input checked="checked" type="radio" name="beatle" value="J" /> John</p>
802 <p><input type="radio" name="beatle" value="P" /> Paul</p>
803 <p><input type="radio" name="beatle" value="G" /> George</p>
804 <p><input type="radio" name="beatle" value="R" /> Ringo</p>
805 >>> for inp in r:
806 ... print '%s %s %s %s %s' % (inp.name, inp.value, inp.choice_value, inp.choice_label, inp.is_checked())
807 beatle J J John True
808 beatle J P Paul False
809 beatle J G George False
810 beatle J R Ringo False
811
812 You can create your own custom renderers for RadioSelect to use.
813 >>> class MyRenderer(RadioFieldRenderer):
814 ...    def render(self):
815 ...        return u'<br />\n'.join([unicode(choice) for choice in self])
816 >>> w = RadioSelect(renderer=MyRenderer)
817 >>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
818 <label><input type="radio" name="beatle" value="J" /> John</label><br />
819 <label><input type="radio" name="beatle" value="P" /> Paul</label><br />
820 <label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />