PageRenderTime 451ms CodeModel.GetById 395ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/regressiontests/forms/tests/widgets.py

https://code.google.com/p/mango-py/
Python | 1158 lines | 1145 code | 5 blank | 8 comment | 4 complexity | 6a791d43e857d509e116bb2f1471044d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file