1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
7
from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
8
from django.core.files.uploadedfile import SimpleUploadedFile
9
from django.core.urlresolvers import reverse
10
from django.forms import *
11
from django.forms.widgets import RadioFieldRenderer
12
from django.utils import formats
13
from django.utils.safestring import mark_safe
14
from django.utils import six
15
from django.utils.translation import activate, deactivate
16
from django.test import TestCase
17
from django.test.utils import override_settings
18
from django.utils.encoding import python_2_unicode_compatible
20
from ..models import Article
23
class FormsWidgetTestCase(TestCase):
24
# Each Widget class corresponds to an HTML form widget. A Widget knows how to
25
# render itself, given a field name and some data. Widgets don't perform
27
def test_textinput(self):
29
self.assertHTMLEqual(w.render('email', ''), '<input type="text" name="email" />')
30
self.assertHTMLEqual(w.render('email', None), '<input type="text" name="email" />')
31
self.assertHTMLEqual(w.render('email', 'test@example.com'), '<input type="text" name="email" value="test@example.com" />')
32
self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '<input type="text" name="email" value="some "quoted" & ampersanded value" />')
33
self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '<input type="text" name="email" value="test@example.com" class="fun" />')
35
self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<input type="text" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" class="fun" />')
37
# You can also pass 'attrs' to the constructor:
38
w = TextInput(attrs={'class': 'fun', 'type': 'email'})
39
self.assertHTMLEqual(w.render('email', ''), '<input type="email" class="fun" name="email" />')
40
self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="email" class="fun" value="foo@example.com" name="email" />')
42
# 'attrs' passed to render() get precedence over those passed to the constructor:
43
w = TextInput(attrs={'class': 'pretty'})
44
self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '<input type="text" class="special" name="email" />')
46
# 'attrs' can be safe-strings if needed)
47
w = TextInput(attrs={'onBlur': mark_safe("function('foo')")})
48
self.assertHTMLEqual(w.render('email', ''), '<input onBlur="function(\'foo\')" type="text" name="email" />')
50
def test_passwordinput(self):
52
self.assertHTMLEqual(w.render('email', ''), '<input type="password" name="email" />')
53
self.assertHTMLEqual(w.render('email', None), '<input type="password" name="email" />')
54
self.assertHTMLEqual(w.render('email', 'secret'), '<input type="password" name="email" />')
56
# The render_value argument lets you specify whether the widget should render
57
# its value. For security reasons, this is off by default.
58
w = PasswordInput(render_value=True)
59
self.assertHTMLEqual(w.render('email', ''), '<input type="password" name="email" />')
60
self.assertHTMLEqual(w.render('email', None), '<input type="password" name="email" />')
61
self.assertHTMLEqual(w.render('email', 'test@example.com'), '<input type="password" name="email" value="test@example.com" />')
62
self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '<input type="password" name="email" value="some "quoted" & ampersanded value" />')
63
self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '<input type="password" name="email" value="test@example.com" class="fun" />')
65
# You can also pass 'attrs' to the constructor:
66
w = PasswordInput(attrs={'class': 'fun'}, render_value=True)
67
self.assertHTMLEqual(w.render('email', ''), '<input type="password" class="fun" name="email" />')
68
self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="password" class="fun" value="foo@example.com" name="email" />')
70
# 'attrs' passed to render() get precedence over those passed to the constructor:
71
w = PasswordInput(attrs={'class': 'pretty'}, render_value=True)
72
self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '<input type="password" class="special" name="email" />')
74
self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />')
76
def test_hiddeninput(self):
78
self.assertHTMLEqual(w.render('email', ''), '<input type="hidden" name="email" />')
79
self.assertHTMLEqual(w.render('email', None), '<input type="hidden" name="email" />')
80
self.assertHTMLEqual(w.render('email', 'test@example.com'), '<input type="hidden" name="email" value="test@example.com" />')
81
self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '<input type="hidden" name="email" value="some "quoted" & ampersanded value" />')
82
self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '<input type="hidden" name="email" value="test@example.com" class="fun" />')
84
# You can also pass 'attrs' to the constructor:
85
w = HiddenInput(attrs={'class': 'fun'})
86
self.assertHTMLEqual(w.render('email', ''), '<input type="hidden" class="fun" name="email" />')
87
self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="hidden" class="fun" value="foo@example.com" name="email" />')
89
# 'attrs' passed to render() get precedence over those passed to the constructor:
90
w = HiddenInput(attrs={'class': 'pretty'})
91
self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '<input type="hidden" class="special" name="email" />')
93
self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />')
95
# 'attrs' passed to render() get precedence over those passed to the constructor:
96
w = HiddenInput(attrs={'class': 'pretty'})
97
self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '<input type="hidden" class="special" name="email" />')
99
# Boolean values are rendered to their string forms ("True" and "False").
101
self.assertHTMLEqual(w.render('get_spam', False), '<input type="hidden" name="get_spam" value="False" />')
102
self.assertHTMLEqual(w.render('get_spam', True), '<input type="hidden" name="get_spam" value="True" />')
104
def test_multiplehiddeninput(self):
105
w = MultipleHiddenInput()
106
self.assertHTMLEqual(w.render('email', []), '')
107
self.assertHTMLEqual(w.render('email', None), '')
108
self.assertHTMLEqual(w.render('email', ['test@example.com']), '<input type="hidden" name="email" value="test@example.com" />')
109
self.assertHTMLEqual(w.render('email', ['some "quoted" & ampersanded value']), '<input type="hidden" name="email" value="some "quoted" & ampersanded value" />')
110
self.assertHTMLEqual(w.render('email', ['test@example.com', 'foo@example.com']), '<input type="hidden" name="email" value="test@example.com" />\n<input type="hidden" name="email" value="foo@example.com" />')
111
self.assertHTMLEqual(w.render('email', ['test@example.com'], attrs={'class': 'fun'}), '<input type="hidden" name="email" value="test@example.com" class="fun" />')
112
self.assertHTMLEqual(w.render('email', ['test@example.com', 'foo@example.com'], attrs={'class': 'fun'}), '<input type="hidden" name="email" value="test@example.com" class="fun" />\n<input type="hidden" name="email" value="foo@example.com" class="fun" />')
114
# You can also pass 'attrs' to the constructor:
115
w = MultipleHiddenInput(attrs={'class': 'fun'})
116
self.assertHTMLEqual(w.render('email', []), '')
117
self.assertHTMLEqual(w.render('email', ['foo@example.com']), '<input type="hidden" class="fun" value="foo@example.com" name="email" />')
118
self.assertHTMLEqual(w.render('email', ['foo@example.com', 'test@example.com']), '<input type="hidden" class="fun" value="foo@example.com" name="email" />\n<input type="hidden" class="fun" value="test@example.com" name="email" />')
120
# 'attrs' passed to render() get precedence over those passed to the constructor:
121
w = MultipleHiddenInput(attrs={'class': 'pretty'})
122
self.assertHTMLEqual(w.render('email', ['foo@example.com'], attrs={'class': 'special'}), '<input type="hidden" class="special" value="foo@example.com" name="email" />')
124
self.assertHTMLEqual(w.render('email', ['ŠĐĆŽćžšđ'], attrs={'class': 'fun'}), '<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />')
126
# 'attrs' passed to render() get precedence over those passed to the constructor:
127
w = MultipleHiddenInput(attrs={'class': 'pretty'})
128
self.assertHTMLEqual(w.render('email', ['foo@example.com'], attrs={'class': 'special'}), '<input type="hidden" class="special" value="foo@example.com" name="email" />')
130
# Each input gets a separate ID.
131
w = MultipleHiddenInput()
132
self.assertHTMLEqual(w.render('letters', list('abc'), attrs={'id': 'hideme'}), '<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" />')
134
def test_fileinput(self):
135
# FileInput widgets don't ever show the value, because the old value is of no use
136
# if you are updating the form or if the provided file generated an error.
138
self.assertHTMLEqual(w.render('email', ''), '<input type="file" name="email" />')
139
self.assertHTMLEqual(w.render('email', None), '<input type="file" name="email" />')
140
self.assertHTMLEqual(w.render('email', 'test@example.com'), '<input type="file" name="email" />')
141
self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '<input type="file" name="email" />')
142
self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '<input type="file" name="email" class="fun" />')
144
# You can also pass 'attrs' to the constructor:
145
w = FileInput(attrs={'class': 'fun'})
146
self.assertHTMLEqual(w.render('email', ''), '<input type="file" class="fun" name="email" />')
147
self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="file" class="fun" name="email" />')
149
self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<input type="file" class="fun" name="email" />')
151
# Test for the behavior of _has_changed for FileInput. The value of data will
152
# more than likely come from request.FILES. The value of initial data will
153
# likely be a filename stored in the database. Since its value is of no use to
154
# a FileInput it is ignored.
157
# No file was uploaded and no initial data.
158
self.assertFalse(w._has_changed('', None))
160
# A file was uploaded and no initial data.
161
self.assertTrue(w._has_changed('', {'filename': 'resume.txt', 'content': 'My resume'}))
163
# A file was not uploaded, but there is initial data
164
self.assertFalse(w._has_changed('resume.txt', None))
166
# A file was uploaded and there is initial data (file identity is not dealt
168
self.assertTrue(w._has_changed('resume.txt', {'filename': 'resume.txt', 'content': 'My resume'}))
170
def test_textarea(self):
172
self.assertHTMLEqual(w.render('msg', ''), '<textarea rows="10" cols="40" name="msg"></textarea>')
173
self.assertHTMLEqual(w.render('msg', None), '<textarea rows="10" cols="40" name="msg"></textarea>')
174
self.assertHTMLEqual(w.render('msg', 'value'), '<textarea rows="10" cols="40" name="msg">value</textarea>')
175
self.assertHTMLEqual(w.render('msg', 'some "quoted" & ampersanded value'), '<textarea rows="10" cols="40" name="msg">some "quoted" & ampersanded value</textarea>')
176
self.assertHTMLEqual(w.render('msg', mark_safe('pre "quoted" value')), '<textarea rows="10" cols="40" name="msg">pre "quoted" value</textarea>')
177
self.assertHTMLEqual(w.render('msg', 'value', attrs={'class': 'pretty', 'rows': 20}), '<textarea class="pretty" rows="20" cols="40" name="msg">value</textarea>')
179
# You can also pass 'attrs' to the constructor:
180
w = Textarea(attrs={'class': 'pretty'})
181
self.assertHTMLEqual(w.render('msg', ''), '<textarea rows="10" cols="40" name="msg" class="pretty"></textarea>')
182
self.assertHTMLEqual(w.render('msg', 'example'), '<textarea rows="10" cols="40" name="msg" class="pretty">example</textarea>')
184
# 'attrs' passed to render() get precedence over those passed to the constructor:
185
w = Textarea(attrs={'class': 'pretty'})
186
self.assertHTMLEqual(w.render('msg', '', attrs={'class': 'special'}), '<textarea rows="10" cols="40" name="msg" class="special"></textarea>')
188
self.assertHTMLEqual(w.render('msg', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<textarea rows="10" cols="40" name="msg" class="fun">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</textarea>')
190
def test_checkboxinput(self):
192
self.assertHTMLEqual(w.render('is_cool', ''), '<input type="checkbox" name="is_cool" />')
193
self.assertHTMLEqual(w.render('is_cool', None), '<input type="checkbox" name="is_cool" />')
194
self.assertHTMLEqual(w.render('is_cool', False), '<input type="checkbox" name="is_cool" />')
195
self.assertHTMLEqual(w.render('is_cool', True), '<input checked="checked" type="checkbox" name="is_cool" />')
197
# Using any value that's not in ('', None, False, True) will check the checkbox
198
# and set the 'value' attribute.
199
self.assertHTMLEqual(w.render('is_cool', 'foo'), '<input checked="checked" type="checkbox" name="is_cool" value="foo" />')
201
self.assertHTMLEqual(w.render('is_cool', False, attrs={'class': 'pretty'}), '<input type="checkbox" name="is_cool" class="pretty" />')
203
# regression for #17114
204
self.assertHTMLEqual(w.render('is_cool', 0), '<input checked="checked" type="checkbox" name="is_cool" value="0" />')
205
self.assertHTMLEqual(w.render('is_cool', 1), '<input checked="checked" type="checkbox" name="is_cool" value="1" />')
207
# You can also pass 'attrs' to the constructor:
208
w = CheckboxInput(attrs={'class': 'pretty'})
209
self.assertHTMLEqual(w.render('is_cool', ''), '<input type="checkbox" class="pretty" name="is_cool" />')
211
# 'attrs' passed to render() get precedence over those passed to the constructor:
212
w = CheckboxInput(attrs={'class': 'pretty'})
213
self.assertHTMLEqual(w.render('is_cool', '', attrs={'class': 'special'}), '<input type="checkbox" class="special" name="is_cool" />')
215
# You can pass 'check_test' to the constructor. This is a callable that takes the
216
# value and returns True if the box should be checked.
217
w = CheckboxInput(check_test=lambda value: value.startswith('hello'))
218
self.assertHTMLEqual(w.render('greeting', ''), '<input type="checkbox" name="greeting" />')
219
self.assertHTMLEqual(w.render('greeting', 'hello'), '<input checked="checked" type="checkbox" name="greeting" value="hello" />')
220
self.assertHTMLEqual(w.render('greeting', 'hello there'), '<input checked="checked" type="checkbox" name="greeting" value="hello there" />')
221
self.assertHTMLEqual(w.render('greeting', 'hello & goodbye'), '<input checked="checked" type="checkbox" name="greeting" value="hello & goodbye" />')
223
# Ticket #17888: calling check_test shouldn't swallow exceptions
224
with self.assertRaises(AttributeError):
225
w.render('greeting', True)
227
# The CheckboxInput widget will return False if the key is not found in the data
228
# dictionary (because HTML form submission doesn't send any result for unchecked
230
self.assertFalse(w.value_from_datadict({}, {}, 'testing'))
232
value = w.value_from_datadict({'testing': '0'}, {}, 'testing')
233
self.assertIsInstance(value, bool)
234
self.assertTrue(value)
236
self.assertFalse(w._has_changed(None, None))
237
self.assertFalse(w._has_changed(None, ''))
238
self.assertFalse(w._has_changed('', None))
239
self.assertFalse(w._has_changed('', ''))
240
self.assertTrue(w._has_changed(False, 'on'))
241
self.assertFalse(w._has_changed(True, 'on'))
242
self.assertTrue(w._has_changed(True, ''))
243
# Initial value may have mutated to a string due to show_hidden_initial (#19537)
244
self.assertTrue(w._has_changed('False', 'on'))
246
def test_select(self):
248
self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select name="beatle">
249
<option value="J" selected="selected">John</option>
250
<option value="P">Paul</option>
251
<option value="G">George</option>
252
<option value="R">Ringo</option>
255
# If the value is None, none of the options are selected:
256
self.assertHTMLEqual(w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select name="beatle">
257
<option value="J">John</option>
258
<option value="P">Paul</option>
259
<option value="G">George</option>
260
<option value="R">Ringo</option>
263
# If the value corresponds to a label (but not to an option value), none of the options are selected:
264
self.assertHTMLEqual(w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select name="beatle">
265
<option value="J">John</option>
266
<option value="P">Paul</option>
267
<option value="G">George</option>
268
<option value="R">Ringo</option>
271
# Only one option can be selected, see #8103:
272
self.assertHTMLEqual(w.render('choices', '0', choices=(('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('0', 'extra'))), """<select name="choices">
273
<option value="0" selected="selected">0</option>
274
<option value="1">1</option>
275
<option value="2">2</option>
276
<option value="3">3</option>
277
<option value="0">extra</option>
280
# The value is compared to its str():
281
self.assertHTMLEqual(w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]), """<select name="num">
282
<option value="1">1</option>
283
<option value="2" selected="selected">2</option>
284
<option value="3">3</option>
286
self.assertHTMLEqual(w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]), """<select name="num">
287
<option value="1">1</option>
288
<option value="2" selected="selected">2</option>
289
<option value="3">3</option>
291
self.assertHTMLEqual(w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]), """<select name="num">
292
<option value="1">1</option>
293
<option value="2" selected="selected">2</option>
294
<option value="3">3</option>
297
# The 'choices' argument can be any iterable:
298
from itertools import chain
302
self.assertHTMLEqual(w.render('num', 2, choices=get_choices()), """<select name="num">
303
<option value="0">0</option>
304
<option value="1">1</option>
305
<option value="2" selected="selected">2</option>
306
<option value="3">3</option>
307
<option value="4">4</option>
309
things = ({'id': 1, 'name': 'And Boom'}, {'id': 2, 'name': 'One More Thing!'})
310
class SomeForm(Form):
311
somechoice = ChoiceField(choices=chain((('', '-'*9),), [(thing['id'], thing['name']) for thing in things]))
313
self.assertHTMLEqual(f.as_table(), '<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>')
314
self.assertHTMLEqual(f.as_table(), '<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>')
315
f = SomeForm({'somechoice': 2})
316
self.assertHTMLEqual(f.as_table(), '<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>')
318
# You can also pass 'choices' to the constructor:
319
w = Select(choices=[(1, 1), (2, 2), (3, 3)])
320
self.assertHTMLEqual(w.render('num', 2), """<select name="num">
321
<option value="1">1</option>
322
<option value="2" selected="selected">2</option>
323
<option value="3">3</option>
326
# If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
327
self.assertHTMLEqual(w.render('num', 2, choices=[(4, 4), (5, 5)]), """<select name="num">
328
<option value="1">1</option>
329
<option value="2" selected="selected">2</option>
330
<option value="3">3</option>
331
<option value="4">4</option>
332
<option value="5">5</option>
335
# Choices are escaped correctly
336
self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """<select name="escape">
337
<option value="1">1</option>
338
<option value="2">2</option>
339
<option value="3">3</option>
340
<option value="bad">you & me</option>
341
<option value="good">you > me</option>
344
# Unicode choices are correctly rendered as HTML
345
self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]), '<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>')
347
# If choices is passed to the constructor and is a generator, it can be iterated
348
# over multiple times without getting consumed:
349
w = Select(choices=get_choices())
350
self.assertHTMLEqual(w.render('num', 2), """<select name="num">
351
<option value="0">0</option>
352
<option value="1">1</option>
353
<option value="2" selected="selected">2</option>
354
<option value="3">3</option>
355
<option value="4">4</option>
357
self.assertHTMLEqual(w.render('num', 3), """<select name="num">
358
<option value="0">0</option>
359
<option value="1">1</option>
360
<option value="2">2</option>
361
<option value="3" selected="selected">3</option>
362
<option value="4">4</option>
365
# Choices can be nested one level in order to create HTML optgroups:
366
w.choices=(('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
367
self.assertHTMLEqual(w.render('nestchoice', None), """<select name="nestchoice">
368
<option value="outer1">Outer 1</option>
369
<optgroup label="Group "1"">
370
<option value="inner1">Inner 1</option>
371
<option value="inner2">Inner 2</option>
375
self.assertHTMLEqual(w.render('nestchoice', 'outer1'), """<select name="nestchoice">
376
<option value="outer1" selected="selected">Outer 1</option>
377
<optgroup label="Group "1"">
378
<option value="inner1">Inner 1</option>
379
<option value="inner2">Inner 2</option>
383
self.assertHTMLEqual(w.render('nestchoice', 'inner1'), """<select name="nestchoice">
384
<option value="outer1">Outer 1</option>
385
<optgroup label="Group "1"">
386
<option value="inner1" selected="selected">Inner 1</option>
387
<option value="inner2">Inner 2</option>
391
def test_nullbooleanselect(self):
392
w = NullBooleanSelect()
393
self.assertTrue(w.render('is_cool', True), """<select name="is_cool">
394
<option value="1">Unknown</option>
395
<option value="2" selected="selected">Yes</option>
396
<option value="3">No</option>
398
self.assertHTMLEqual(w.render('is_cool', False), """<select name="is_cool">
399
<option value="1">Unknown</option>
400
<option value="2">Yes</option>
401
<option value="3" selected="selected">No</option>
403
self.assertHTMLEqual(w.render('is_cool', None), """<select name="is_cool">
404
<option value="1" selected="selected">Unknown</option>
405
<option value="2">Yes</option>
406
<option value="3">No</option>
408
self.assertHTMLEqual(w.render('is_cool', '2'), """<select name="is_cool">
409
<option value="1">Unknown</option>
410
<option value="2" selected="selected">Yes</option>
411
<option value="3">No</option>
413
self.assertHTMLEqual(w.render('is_cool', '3'), """<select name="is_cool">
414
<option value="1">Unknown</option>
415
<option value="2">Yes</option>
416
<option value="3" selected="selected">No</option>
418
self.assertTrue(w._has_changed(False, None))
419
self.assertTrue(w._has_changed(None, False))
420
self.assertFalse(w._has_changed(None, None))
421
self.assertFalse(w._has_changed(False, False))
422
self.assertTrue(w._has_changed(True, False))
423
self.assertTrue(w._has_changed(True, None))
424
self.assertTrue(w._has_changed(True, False))
426
def test_selectmultiple(self):
428
self.assertHTMLEqual(w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select multiple="multiple" name="beatles">
429
<option value="J" selected="selected">John</option>
430
<option value="P">Paul</option>
431
<option value="G">George</option>
432
<option value="R">Ringo</option>
434
self.assertHTMLEqual(w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select multiple="multiple" name="beatles">
435
<option value="J" selected="selected">John</option>
436
<option value="P" selected="selected">Paul</option>
437
<option value="G">George</option>
438
<option value="R">Ringo</option>
440
self.assertHTMLEqual(w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select multiple="multiple" name="beatles">
441
<option value="J" selected="selected">John</option>
442
<option value="P" selected="selected">Paul</option>
443
<option value="G">George</option>
444
<option value="R" selected="selected">Ringo</option>
447
# If the value is None, none of the options are selected:
448
self.assertHTMLEqual(w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select multiple="multiple" name="beatles">
449
<option value="J">John</option>
450
<option value="P">Paul</option>
451
<option value="G">George</option>
452
<option value="R">Ringo</option>
455
# If the value corresponds to a label (but not to an option value), none of the options are selected:
456
self.assertHTMLEqual(w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select multiple="multiple" name="beatles">
457
<option value="J">John</option>
458
<option value="P">Paul</option>
459
<option value="G">George</option>
460
<option value="R">Ringo</option>
463
# Multiple options (with the same value) can be selected, see #8103:
464
self.assertHTMLEqual(w.render('choices', ['0'], choices=(('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('0', 'extra'))), """<select multiple="multiple" name="choices">
465
<option value="0" selected="selected">0</option>
466
<option value="1">1</option>
467
<option value="2">2</option>
468
<option value="3">3</option>
469
<option value="0" selected="selected">extra</option>
472
# If multiple values are given, but some of them are not valid, the valid ones are selected:
473
self.assertHTMLEqual(w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<select multiple="multiple" name="beatles">
474
<option value="J" selected="selected">John</option>
475
<option value="P">Paul</option>
476
<option value="G" selected="selected">George</option>
477
<option value="R">Ringo</option>
480
# The value is compared to its str():
481
self.assertHTMLEqual(w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]), """<select multiple="multiple" name="nums">
482
<option value="1">1</option>
483
<option value="2" selected="selected">2</option>
484
<option value="3">3</option>
486
self.assertHTMLEqual(w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]), """<select multiple="multiple" name="nums">
487
<option value="1">1</option>
488
<option value="2" selected="selected">2</option>
489
<option value="3">3</option>
491
self.assertHTMLEqual(w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]), """<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>
497
# The 'choices' argument can be any iterable:
501
self.assertHTMLEqual(w.render('nums', [2], choices=get_choices()), """<select multiple="multiple" name="nums">
502
<option value="0">0</option>
503
<option value="1">1</option>
504
<option value="2" selected="selected">2</option>
505
<option value="3">3</option>
506
<option value="4">4</option>
509
# You can also pass 'choices' to the constructor:
510
w = SelectMultiple(choices=[(1, 1), (2, 2), (3, 3)])
511
self.assertHTMLEqual(w.render('nums', [2]), """<select multiple="multiple" name="nums">
512
<option value="1">1</option>
513
<option value="2" selected="selected">2</option>
514
<option value="3">3</option>
517
# If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
518
self.assertHTMLEqual(w.render('nums', [2], choices=[(4, 4), (5, 5)]), """<select multiple="multiple" name="nums">
519
<option value="1">1</option>
520
<option value="2" selected="selected">2</option>
521
<option value="3">3</option>
522
<option value="4">4</option>
523
<option value="5">5</option>
526
# Choices are escaped correctly
527
self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """<select multiple="multiple" name="escape">
528
<option value="1">1</option>
529
<option value="2">2</option>
530
<option value="3">3</option>
531
<option value="bad">you & me</option>
532
<option value="good">you > me</option>
535
# Unicode choices are correctly rendered as HTML
536
self.assertHTMLEqual(w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]), '<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>')
538
# Test the usage of _has_changed
539
self.assertFalse(w._has_changed(None, None))
540
self.assertFalse(w._has_changed([], None))
541
self.assertTrue(w._has_changed(None, ['1']))
542
self.assertFalse(w._has_changed([1, 2], ['1', '2']))
543
self.assertTrue(w._has_changed([1, 2], ['1']))
544
self.assertTrue(w._has_changed([1, 2], ['1', '3']))
546
# Choices can be nested one level in order to create HTML optgroups:
547
w.choices = (('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
548
self.assertHTMLEqual(w.render('nestchoice', None), """<select multiple="multiple" name="nestchoice">
549
<option value="outer1">Outer 1</option>
550
<optgroup label="Group "1"">
551
<option value="inner1">Inner 1</option>
552
<option value="inner2">Inner 2</option>
556
self.assertHTMLEqual(w.render('nestchoice', ['outer1']), """<select multiple="multiple" name="nestchoice">
557
<option value="outer1" selected="selected">Outer 1</option>
558
<optgroup label="Group "1"">
559
<option value="inner1">Inner 1</option>
560
<option value="inner2">Inner 2</option>
564
self.assertHTMLEqual(w.render('nestchoice', ['inner1']), """<select multiple="multiple" name="nestchoice">
565
<option value="outer1">Outer 1</option>
566
<optgroup label="Group "1"">
567
<option value="inner1" selected="selected">Inner 1</option>
568
<option value="inner2">Inner 2</option>
572
self.assertHTMLEqual(w.render('nestchoice', ['outer1', 'inner2']), """<select multiple="multiple" name="nestchoice">
573
<option value="outer1" selected="selected">Outer 1</option>
574
<optgroup label="Group "1"">
575
<option value="inner1">Inner 1</option>
576
<option value="inner2" selected="selected">Inner 2</option>
580
def test_radioselect(self):
582
self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
583
<li><label><input checked="checked" type="radio" name="beatle" value="J" /> John</label></li>
584
<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
585
<li><label><input type="radio" name="beatle" value="G" /> George</label></li>
586
<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
589
# If the value is None, none of the options are checked:
590
self.assertHTMLEqual(w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
591
<li><label><input type="radio" name="beatle" value="J" /> John</label></li>
592
<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
593
<li><label><input type="radio" name="beatle" value="G" /> George</label></li>
594
<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
597
# If the value corresponds to a label (but not to an option value), none of the options are checked:
598
self.assertHTMLEqual(w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
599
<li><label><input type="radio" name="beatle" value="J" /> John</label></li>
600
<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
601
<li><label><input type="radio" name="beatle" value="G" /> George</label></li>
602
<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
605
# The value is compared to its str():
606
self.assertHTMLEqual(w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]), """<ul>
607
<li><label><input type="radio" name="num" value="1" /> 1</label></li>
608
<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
609
<li><label><input type="radio" name="num" value="3" /> 3</label></li>
611
self.assertHTMLEqual(w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]), """<ul>
612
<li><label><input type="radio" name="num" value="1" /> 1</label></li>
613
<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
614
<li><label><input type="radio" name="num" value="3" /> 3</label></li>
616
self.assertHTMLEqual(w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]), """<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>
622
# The 'choices' argument can be any iterable:
626
self.assertHTMLEqual(w.render('num', 2, choices=get_choices()), """<ul>
627
<li><label><input type="radio" name="num" value="0" /> 0</label></li>
628
<li><label><input type="radio" name="num" value="1" /> 1</label></li>
629
<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
630
<li><label><input type="radio" name="num" value="3" /> 3</label></li>
631
<li><label><input type="radio" name="num" value="4" /> 4</label></li>
634
# You can also pass 'choices' to the constructor:
635
w = RadioSelect(choices=[(1, 1), (2, 2), (3, 3)])
636
self.assertHTMLEqual(w.render('num', 2), """<ul>
637
<li><label><input type="radio" name="num" value="1" /> 1</label></li>
638
<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
639
<li><label><input type="radio" name="num" value="3" /> 3</label></li>
642
# If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
643
self.assertHTMLEqual(w.render('num', 2, choices=[(4, 4), (5, 5)]), """<ul>
644
<li><label><input type="radio" name="num" value="1" /> 1</label></li>
645
<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
646
<li><label><input type="radio" name="num" value="3" /> 3</label></li>
647
<li><label><input type="radio" name="num" value="4" /> 4</label></li>
648
<li><label><input type="radio" name="num" value="5" /> 5</label></li>
651
# RadioSelect uses a RadioFieldRenderer to render the individual radio inputs.
652
# You can manipulate that object directly to customize the way the RadioSelect
655
r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
662
inp_set1.append(str(inp))
663
inp_set2.append('%s<br />' % inp)
664
inp_set3.append('<p>%s %s</p>' % (inp.tag(), inp.choice_label))
665
inp_set4.append('%s %s %s %s %s' % (inp.name, inp.value, inp.choice_value, inp.choice_label, inp.is_checked()))
667
self.assertHTMLEqual('\n'.join(inp_set1), """<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
668
<label><input type="radio" name="beatle" value="P" /> Paul</label>
669
<label><input type="radio" name="beatle" value="G" /> George</label>
670
<label><input type="radio" name="beatle" value="R" /> Ringo</label>""")
671
self.assertHTMLEqual('\n'.join(inp_set2), """<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label><br />
672
<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
673
<label><input type="radio" name="beatle" value="G" /> George</label><br />
674
<label><input type="radio" name="beatle" value="R" /> Ringo</label><br />""")
675
self.assertHTMLEqual('\n'.join(inp_set3), """<p><input checked="checked" type="radio" name="beatle" value="J" /> John</p>
676
<p><input type="radio" name="beatle" value="P" /> Paul</p>
677
<p><input type="radio" name="beatle" value="G" /> George</p>
678
<p><input type="radio" name="beatle" value="R" /> Ringo</p>""")
679
self.assertHTMLEqual('\n'.join(inp_set4), """beatle J J John True
680
beatle J P Paul False
681
beatle J G George False
682
beatle J R Ringo False""")
684
# You can create your own custom renderers for RadioSelect to use.
685
class MyRenderer(RadioFieldRenderer):
687
return '<br />\n'.join([six.text_type(choice) for choice in self])
688
w = RadioSelect(renderer=MyRenderer)
689
self.assertHTMLEqual(w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<label><input type="radio" name="beatle" value="J" /> John</label><br />
690
<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
691
<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
692
<label><input type="radio" name="beatle" value="R" /> Ringo</label>""")
694
# Or you can use custom RadioSelect fields that use your custom renderer.
695
class CustomRadioSelect(RadioSelect):
696
renderer = MyRenderer
697
w = CustomRadioSelect()
698
self.assertHTMLEqual(w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<label><input type="radio" name="beatle" value="J" /> John</label><br />
699
<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
700
<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
701
<label><input type="radio" name="beatle" value="R" /> Ringo</label>""")
703
# A RadioFieldRenderer object also allows index access to individual RadioInput
705
r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
706
self.assertHTMLEqual(str(r[1]), '<label><input type="radio" name="beatle" value="P" /> Paul</label>')
707
self.assertHTMLEqual(str(r[0]), '<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>')
708
self.assertTrue(r[0].is_checked())
709
self.assertFalse(r[1].is_checked())
710
self.assertEqual((r[1].name, r[1].value, r[1].choice_value, r[1].choice_label), ('beatle', 'J', 'P', 'Paul'))
714
self.fail("This offset should not exist.")
718
# Choices are escaped correctly
720
self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """<ul>
721
<li><label><input type="radio" name="escape" value="bad" /> you & me</label></li>
722
<li><label><input type="radio" name="escape" value="good" /> you > me</label></li>
725
# Unicode choices are correctly rendered as HTML
727
self.assertHTMLEqual(six.text_type(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])), '<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>')
729
# Attributes provided at instantiation are passed to the constituent inputs
730
w = RadioSelect(attrs={'id':'foo'})
731
self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
732
<li><label for="foo_0"><input checked="checked" type="radio" id="foo_0" value="J" name="beatle" /> John</label></li>
733
<li><label for="foo_1"><input type="radio" id="foo_1" value="P" name="beatle" /> Paul</label></li>
734
<li><label for="foo_2"><input type="radio" id="foo_2" value="G" name="beatle" /> George</label></li>
735
<li><label for="foo_3"><input type="radio" id="foo_3" value="R" name="beatle" /> Ringo</label></li>
738
# Attributes provided at render-time are passed to the constituent inputs
740
self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')), attrs={'id':'bar'}), """<ul>
741
<li><label for="bar_0"><input checked="checked" type="radio" id="bar_0" value="J" name="beatle" /> John</label></li>
742
<li><label for="bar_1"><input type="radio" id="bar_1" value="P" name="beatle" /> Paul</label></li>
743
<li><label for="bar_2"><input type="radio" id="bar_2" value="G" name="beatle" /> George</label></li>
744
<li><label for="bar_3"><input type="radio" id="bar_3" value="R" name="beatle" /> Ringo</label></li>
747
def test_checkboxselectmultiple(self):
748
w = CheckboxSelectMultiple()
749
self.assertHTMLEqual(w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
750
<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li>
751
<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li>
752
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
753
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
755
self.assertHTMLEqual(w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
756
<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li>
757
<li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li>
758
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
759
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
761
self.assertHTMLEqual(w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
762
<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li>
763
<li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li>
764
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
765
<li><label><input checked="checked" type="checkbox" name="beatles" value="R" /> Ringo</label></li>
768
# If the value is None, none of the options are selected:
769
self.assertHTMLEqual(w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
770
<li><label><input type="checkbox" name="beatles" value="J" /> John</label></li>
771
<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li>
772
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
773
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
776
# If the value corresponds to a label (but not to an option value), none of the options are selected:
777
self.assertHTMLEqual(w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
778
<li><label><input type="checkbox" name="beatles" value="J" /> John</label></li>
779
<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li>
780
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
781
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
784
# If multiple values are given, but some of them are not valid, the valid ones are selected:
785
self.assertHTMLEqual(w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>
786
<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li>
787
<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li>
788
<li><label><input checked="checked" type="checkbox" name="beatles" value="G" /> George</label></li>
789
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
792
# The value is compared to its str():
793
self.assertHTMLEqual(w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]), """<ul>
794
<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>
795
<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li>
796
<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>
798
self.assertHTMLEqual(w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]), """<ul>
799
<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>
800
<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li>
801
<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>
803
self.assertHTMLEqual(w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]), """<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>
809
# The 'choices' argument can be any iterable:
813
self.assertHTMLEqual(w.render('nums', [2], choices=get_choices()), """<ul>
814
<li><label><input type="checkbox" name="nums" value="0" /> 0</label></li>
815
<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>
816
<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li>
817
<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>
818
<li><label><input type="checkbox" name="nums" value="4" /> 4</label></li>
821
# You can also pass 'choices' to the constructor:
822
w = CheckboxSelectMultiple(choices=[(1, 1), (2, 2), (3, 3)])
823
self.assertHTMLEqual(w.render('nums', [2]), """<ul>
824
<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>
825
<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li>
826
<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>
829
# If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
830
self.assertHTMLEqual(w.render('nums', [2], choices=[(4, 4), (5, 5)]), """<ul>
831
<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>
832
<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li>
833
<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>
834
<li><label><input type="checkbox" name="nums" value="4" /> 4</label></li>
835
<li><label><input type="checkbox" name="nums" value="5" /> 5</label></li>
838
# Choices are escaped correctly
839
self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """<ul>
840
<li><label><input type="checkbox" name="escape" value="1" /> 1</label></li>
841
<li><label><input type="checkbox" name="escape" value="2" /> 2</label></li>
842
<li><label><input type="checkbox" name="escape" value="3" /> 3</label></li>
843
<li><label><input type="checkbox" name="escape" value="bad" /> you & me</label></li>
844
<li><label><input type="checkbox" name="escape" value="good" /> you > me</label></li>
847
# Test the usage of _has_changed
848
self.assertFalse(w._has_changed(None, None))
849
self.assertFalse(w._has_changed([], None))
850
self.assertTrue(w._has_changed(None, ['1']))
851
self.assertFalse(w._has_changed([1, 2], ['1', '2']))
852
self.assertTrue(w._has_changed([1, 2], ['1']))
853
self.assertTrue(w._has_changed([1, 2], ['1', '3']))
854
self.assertFalse(w._has_changed([2, 1], ['1', '2']))
856
# Unicode choices are correctly rendered as HTML
857
self.assertHTMLEqual(w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]), '<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>\n<li><label><input type="checkbox" name="nums" value="2" /> 2</label></li>\n<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>\n<li><label><input checked="checked" type="checkbox" name="nums" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="checkbox" name="nums" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>')
859
# Each input gets a separate ID
860
self.assertHTMLEqual(CheckboxSelectMultiple().render('letters', list('ac'), choices=zip(list('abc'), list('ABC')), attrs={'id': 'abc'}), """<ul>
861
<li><label for="abc_0"><input checked="checked" type="checkbox" name="letters" value="a" id="abc_0" /> A</label></li>
862
<li><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1" /> B</label></li>
863
<li><label for="abc_2"><input checked="checked" type="checkbox" name="letters" value="c" id="abc_2" /> C</label></li>
866
def test_multi(self):
867
class MyMultiWidget(MultiWidget):
868
def decompress(self, value):
870
return value.split('__')
872
def format_output(self, rendered_widgets):
873
return '<br />'.join(rendered_widgets)
875
w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})))
876
self.assertHTMLEqual(w.render('name', ['john', 'lennon']), '<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />')
877
self.assertHTMLEqual(w.render('name', 'john__lennon'), '<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />')
878
self.assertHTMLEqual(w.render('name', 'john__lennon', attrs={'id':'foo'}), '<input id="foo_0" type="text" class="big" value="john" name="name_0" /><br /><input id="foo_1" type="text" class="small" value="lennon" name="name_1" />')
879
w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})), attrs={'id': 'bar'})
880
self.assertHTMLEqual(w.render('name', ['john', 'lennon']), '<input id="bar_0" type="text" class="big" value="john" name="name_0" /><br /><input id="bar_1" type="text" class="small" value="lennon" name="name_1" />')
882
w = MyMultiWidget(widgets=(TextInput(), TextInput()))
884
# test with no initial data
885
self.assertTrue(w._has_changed(None, ['john', 'lennon']))
887
# test when the data is the same as initial
888
self.assertFalse(w._has_changed('john__lennon', ['john', 'lennon']))
890
# test when the first widget's data has changed
891
self.assertTrue(w._has_changed('john__lennon', ['alfred', 'lennon']))
893
# test when the last widget's data has changed. this ensures that it is not
894
# short circuiting while testing the widgets.
895
self.assertTrue(w._has_changed('john__lennon', ['john', 'denver']))
897
def test_splitdatetime(self):
898
w = SplitDateTimeWidget()
899
self.assertHTMLEqual(w.render('date', ''), '<input type="text" name="date_0" /><input type="text" name="date_1" />')
900
self.assertHTMLEqual(w.render('date', None), '<input type="text" name="date_0" /><input type="text" name="date_1" />')
901
self.assertHTMLEqual(w.render('date', datetime.datetime(2006, 1, 10, 7, 30)), '<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />')
902
self.assertHTMLEqual(w.render('date', [datetime.date(2006, 1, 10), datetime.time(7, 30)]), '<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />')
904
# You can also pass 'attrs' to the constructor. In this case, the attrs will be
905
w = SplitDateTimeWidget(attrs={'class': 'pretty'})
906
self.assertHTMLEqual(w.render('date', datetime.datetime(2006, 1, 10, 7, 30)), '<input type="text" class="pretty" value="2006-01-10" name="date_0" /><input type="text" class="pretty" value="07:30:00" name="date_1" />')
908
# Use 'date_format' and 'time_format' to change the way a value is displayed.
909
w = SplitDateTimeWidget(date_format='%d/%m/%Y', time_format='%H:%M')
910
self.assertHTMLEqual(w.render('date', datetime.datetime(2006, 1, 10, 7, 30)), '<input type="text" name="date_0" value="10/01/2006" /><input type="text" name="date_1" value="07:30" />')
912
self.assertTrue(w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['2008-05-06', '12:40:00']))
913
self.assertFalse(w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:40']))
914
self.assertTrue(w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:41']))
916
def test_datetimeinput(self):
918
self.assertHTMLEqual(w.render('date', None), '<input type="text" name="date" />')
919
d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548)
920
self.assertEqual(str(d), '2007-09-17 12:51:34.482548')
922
# The microseconds are trimmed on display, by default.
923
self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="2007-09-17 12:51:34" />')
924
self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51, 34)), '<input type="text" name="date" value="2007-09-17 12:51:34" />')
925
self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '<input type="text" name="date" value="2007-09-17 12:51:00" />')
927
# Use 'format' to change the way a value is displayed.
928
w = DateTimeInput(format='%d/%m/%Y %H:%M', attrs={'type': 'datetime'})
929
self.assertHTMLEqual(w.render('date', d), '<input type="datetime" name="date" value="17/09/2007 12:51" />')
930
self.assertFalse(w._has_changed(d, '17/09/2007 12:51'))
932
# Make sure a custom format works with _has_changed. The hidden input will use
933
data = datetime.datetime(2010, 3, 6, 12, 0, 0)
934
custom_format = '%d.%m.%Y %H:%M'
935
w = DateTimeInput(format=custom_format)
936
self.assertFalse(w._has_changed(formats.localize_input(data), data.strftime(custom_format)))
938
def test_dateinput(self):
940
self.assertHTMLEqual(w.render('date', None), '<input type="text" name="date" />')
941
d = datetime.date(2007, 9, 17)
942
self.assertEqual(str(d), '2007-09-17')
944
self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="2007-09-17" />')
945
self.assertHTMLEqual(w.render('date', datetime.date(2007, 9, 17)), '<input type="text" name="date" value="2007-09-17" />')
947
# We should be able to initialize from a unicode value.
948
self.assertHTMLEqual(w.render('date', '2007-09-17'), '<input type="text" name="date" value="2007-09-17" />')
950
# Use 'format' to change the way a value is displayed.
951
w = DateInput(format='%d/%m/%Y', attrs={'type': 'date'})
952
self.assertHTMLEqual(w.render('date', d), '<input type="date" name="date" value="17/09/2007" />')
953
self.assertFalse(w._has_changed(d, '17/09/2007'))
955
# Make sure a custom format works with _has_changed. The hidden input will use
956
data = datetime.date(2010, 3, 6)
957
custom_format = '%d.%m.%Y'
958
w = DateInput(format=custom_format)
959
self.assertFalse(w._has_changed(formats.localize_input(data), data.strftime(custom_format)))
961
def test_timeinput(self):
963
self.assertHTMLEqual(w.render('time', None), '<input type="text" name="time" />')
964
t = datetime.time(12, 51, 34, 482548)
965
self.assertEqual(str(t), '12:51:34.482548')
967
# The microseconds are trimmed on display, by default.
968
self.assertHTMLEqual(w.render('time', t), '<input type="text" name="time" value="12:51:34" />')
969
self.assertHTMLEqual(w.render('time', datetime.time(12, 51, 34)), '<input type="text" name="time" value="12:51:34" />')
970
self.assertHTMLEqual(w.render('time', datetime.time(12, 51)), '<input type="text" name="time" value="12:51:00" />')
972
# We should be able to initialize from a unicode value.
973
self.assertHTMLEqual(w.render('time', '13:12:11'), '<input type="text" name="time" value="13:12:11" />')
975
# Use 'format' to change the way a value is displayed.
976
w = TimeInput(format='%H:%M', attrs={'type': 'time'})
977
self.assertHTMLEqual(w.render('time', t), '<input type="time" name="time" value="12:51" />')
978
self.assertFalse(w._has_changed(t, '12:51'))
980
# Make sure a custom format works with _has_changed. The hidden input will use
981
data = datetime.time(13, 0)
982
custom_format = '%I:%M %p'
983
w = TimeInput(format=custom_format)
984
self.assertFalse(w._has_changed(formats.localize_input(data), data.strftime(custom_format)))
986
def test_splithiddendatetime(self):
987
from django.forms.widgets import SplitHiddenDateTimeWidget
989
w = SplitHiddenDateTimeWidget()
990
self.assertHTMLEqual(w.render('date', ''), '<input type="hidden" name="date_0" /><input type="hidden" name="date_1" />')
991
d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548)
992
self.assertHTMLEqual(str(d), '2007-09-17 12:51:34.482548')
993
self.assertHTMLEqual(w.render('date', d), '<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:34" />')
994
self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51, 34)), '<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:34" />')
995
self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:00" />')
998
class NullBooleanSelectLazyForm(Form):
999
"""Form to test for lazy evaluation. Refs #17190"""
1000
bool = BooleanField(widget=NullBooleanSelect())
1002
@override_settings(USE_L10N=True)
1003
class FormsI18NWidgetsTestCase(TestCase):
1005
super(FormsI18NWidgetsTestCase, self).setUp()
1010
super(FormsI18NWidgetsTestCase, self).tearDown()
1012
def test_splitdatetime(self):
1013
w = SplitDateTimeWidget(date_format='%d/%m/%Y', time_format='%H:%M')
1014
self.assertTrue(w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06.05.2008', '12:41']))
1016
def test_datetimeinput(self):
1018
d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548)
1019
w.is_localized = True
1020
self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="17.09.2007 12:51:34" />')
1022
def test_dateinput(self):
1024
d = datetime.date(2007, 9, 17)
1025
w.is_localized = True
1026
self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="17.09.2007" />')
1028
def test_timeinput(self):
1030
t = datetime.time(12, 51, 34, 482548)
1031
w.is_localized = True
1032
self.assertHTMLEqual(w.render('time', t), '<input type="text" name="time" value="12:51:34" />')
1034
def test_splithiddendatetime(self):
1035
from django.forms.widgets import SplitHiddenDateTimeWidget
1037
w = SplitHiddenDateTimeWidget()
1038
w.is_localized = True
1039
self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '<input type="hidden" name="date_0" value="17.09.2007" /><input type="hidden" name="date_1" value="12:51:00" />')
1041
def test_nullbooleanselect(self):
1043
Ensure that the NullBooleanSelect widget's options are lazily
1047
f = NullBooleanSelectLazyForm()
1048
self.assertHTMLEqual(f.fields['bool'].widget.render('id_bool', True), '<select name="id_bool">\n<option value="1">Unbekannt</option>\n<option value="2" selected="selected">Ja</option>\n<option value="3">Nein</option>\n</select>')
1051
class SelectAndTextWidget(MultiWidget):
1053
MultiWidget subclass
1055
def __init__(self, choices=[]):
1057
RadioSelect(choices=choices),
1060
super(SelectAndTextWidget, self).__init__(widgets)
1062
def _set_choices(self, choices):
1064
When choices are set for this widget, we want to pass those along to the Select widget
1066
self.widgets[0].choices = choices
1067
def _get_choices(self):
1069
The choices for this widget are the Select widget's choices
1071
return self.widgets[0].choices
1072
choices = property(_get_choices, _set_choices)
1075
class WidgetTests(TestCase):
1076
def test_12048(self):
1077
# See ticket #12048.
1078
w1 = SelectAndTextWidget(choices=[1,2,3])
1079
w2 = copy.deepcopy(w1)
1080
w2.choices = [4,5,6]
1081
# w2 ought to be independent of w1, since MultiWidget ought
1082
# to make a copy of its sub-widgets when it is copied.
1083
self.assertEqual(w1.choices, [1,2,3])
1085
def test_13390(self):
1087
class SplitDateForm(Form):
1088
field = DateTimeField(widget=SplitDateTimeWidget, required=False)
1090
form = SplitDateForm({'field': ''})
1091
self.assertTrue(form.is_valid())
1092
form = SplitDateForm({'field': ['', '']})
1093
self.assertTrue(form.is_valid())
1095
class SplitDateRequiredForm(Form):
1096
field = DateTimeField(widget=SplitDateTimeWidget, required=True)
1098
form = SplitDateRequiredForm({'field': ''})
1099
self.assertFalse(form.is_valid())
1100
form = SplitDateRequiredForm({'field': ['', '']})
1101
self.assertFalse(form.is_valid())
1104
class LiveWidgetTests(AdminSeleniumWebDriverTestCase):
1105
urls = 'regressiontests.forms.urls'
1107
def test_textarea_trailing_newlines(self):
1109
Test that a roundtrip on a ModelForm doesn't alter the TextField value
1111
article = Article.objects.create(content="\nTst\n")
1112
self.selenium.get('%s%s' % (self.live_server_url,
1113
reverse('article_form', args=[article.pk])))
1114
self.selenium.find_element_by_id('submit').submit()
1115
article = Article.objects.get(pk=article.pk)
1116
# Should be "\nTst\n" after #19251 is fixed
1117
self.assertEqual(article.content, "\r\nTst\r\n")
1120
@python_2_unicode_compatible
1121
class FakeFieldFile(object):
1123
Quacks like a FieldFile (has a .url and unicode representation), but
1124
doesn't require us to care about storages etc.
1132
class ClearableFileInputTests(TestCase):
1133
def test_clear_input_renders(self):
1135
A ClearableFileInput with is_required False and rendered with
1136
an initial value that is a file renders a clear checkbox.
1139
widget = ClearableFileInput()
1140
widget.is_required = False
1141
self.assertHTMLEqual(widget.render('myfile', FakeFieldFile()),
1142
'Currently: <a href="something">something</a> <input type="checkbox" name="myfile-clear" id="myfile-clear_id" /> <label for="myfile-clear_id">Clear</label><br />Change: <input type="file" name="myfile" />')
1144
def test_html_escaped(self):
1146
A ClearableFileInput should escape name, filename and URL when
1147
rendering HTML. Refs #15182.
1150
@python_2_unicode_compatible
1151
class StrangeFieldFile(object):
1152
url = "something?chapter=1§=2©=3&lang=en"
1155
return '''something<div onclick="alert('oops')">.jpg'''
1157
widget = ClearableFileInput()
1158
field = StrangeFieldFile()
1159
output = widget.render('my<div>file', field)
1160
self.assertFalse(field.url in output)
1161
self.assertTrue('href="something?chapter=1&sect=2&copy=3&lang=en"' in output)
1162
self.assertFalse(six.text_type(field) in output)
1163
self.assertTrue('something<div onclick="alert('oops')">.jpg' in output)
1164
self.assertTrue('my<div>file' in output)
1165
self.assertFalse('my<div>file' in output)
1167
def test_clear_input_renders_only_if_not_required(self):
1169
A ClearableFileInput with is_required=False does not render a clear
1173
widget = ClearableFileInput()
1174
widget.is_required = True
1175
self.assertHTMLEqual(widget.render('myfile', FakeFieldFile()),
1176
'Currently: <a href="something">something</a> <br />Change: <input type="file" name="myfile" />')
1178
def test_clear_input_renders_only_if_initial(self):
1180
A ClearableFileInput instantiated with no initial value does not render
1184
widget = ClearableFileInput()
1185
widget.is_required = False
1186
self.assertHTMLEqual(widget.render('myfile', None),
1187
'<input type="file" name="myfile" />')
1189
def test_clear_input_checked_returns_false(self):
1191
ClearableFileInput.value_from_datadict returns False if the clear
1192
checkbox is checked, if not required.
1195
widget = ClearableFileInput()
1196
widget.is_required = False
1197
self.assertEqual(widget.value_from_datadict(
1198
data={'myfile-clear': True},
1200
name='myfile'), False)
1202
def test_clear_input_checked_returns_false_only_if_not_required(self):
1204
ClearableFileInput.value_from_datadict never returns False if the field
1208
widget = ClearableFileInput()
1209
widget.is_required = True
1210
f = SimpleUploadedFile('something.txt', b'content')
1211
self.assertEqual(widget.value_from_datadict(
1212
data={'myfile-clear': True},
1213
files={'myfile': f},