test_selectdatewidget.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. from datetime import date
  2. from django.forms import DateField, Form, SelectDateWidget
  3. from django.test import override_settings
  4. from django.utils import translation
  5. from django.utils.dates import MONTHS_AP
  6. from .base import WidgetTest
  7. class SelectDateWidgetTest(WidgetTest):
  8. maxDiff = None
  9. widget = SelectDateWidget(
  10. years=('2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016'),
  11. )
  12. def test_render_empty(self):
  13. self.check_html(self.widget, 'mydate', '', html=(
  14. """
  15. <select name="mydate_month" id="id_mydate_month">
  16. <option selected value="">---</option>
  17. <option value="1">January</option>
  18. <option value="2">February</option>
  19. <option value="3">March</option>
  20. <option value="4">April</option>
  21. <option value="5">May</option>
  22. <option value="6">June</option>
  23. <option value="7">July</option>
  24. <option value="8">August</option>
  25. <option value="9">September</option>
  26. <option value="10">October</option>
  27. <option value="11">November</option>
  28. <option value="12">December</option>
  29. </select>
  30. <select name="mydate_day" id="id_mydate_day">
  31. <option selected value="">---</option>
  32. <option value="1">1</option>
  33. <option value="2">2</option>
  34. <option value="3">3</option>
  35. <option value="4">4</option>
  36. <option value="5">5</option>
  37. <option value="6">6</option>
  38. <option value="7">7</option>
  39. <option value="8">8</option>
  40. <option value="9">9</option>
  41. <option value="10">10</option>
  42. <option value="11">11</option>
  43. <option value="12">12</option>
  44. <option value="13">13</option>
  45. <option value="14">14</option>
  46. <option value="15">15</option>
  47. <option value="16">16</option>
  48. <option value="17">17</option>
  49. <option value="18">18</option>
  50. <option value="19">19</option>
  51. <option value="20">20</option>
  52. <option value="21">21</option>
  53. <option value="22">22</option>
  54. <option value="23">23</option>
  55. <option value="24">24</option>
  56. <option value="25">25</option>
  57. <option value="26">26</option>
  58. <option value="27">27</option>
  59. <option value="28">28</option>
  60. <option value="29">29</option>
  61. <option value="30">30</option>
  62. <option value="31">31</option>
  63. </select>
  64. <select name="mydate_year" id="id_mydate_year">
  65. <option selected value="">---</option>
  66. <option value="2007">2007</option>
  67. <option value="2008">2008</option>
  68. <option value="2009">2009</option>
  69. <option value="2010">2010</option>
  70. <option value="2011">2011</option>
  71. <option value="2012">2012</option>
  72. <option value="2013">2013</option>
  73. <option value="2014">2014</option>
  74. <option value="2015">2015</option>
  75. <option value="2016">2016</option>
  76. </select>
  77. """
  78. ))
  79. def test_render_none(self):
  80. """
  81. Rendering the None or '' values should yield the same output.
  82. """
  83. self.assertHTMLEqual(
  84. self.widget.render('mydate', None),
  85. self.widget.render('mydate', ''),
  86. )
  87. def test_render_string(self):
  88. self.check_html(self.widget, 'mydate', '2010-04-15', html=(
  89. """
  90. <select name="mydate_month" id="id_mydate_month">
  91. <option value="">---</option>
  92. <option value="1">January</option>
  93. <option value="2">February</option>
  94. <option value="3">March</option>
  95. <option value="4" selected>April</option>
  96. <option value="5">May</option>
  97. <option value="6">June</option>
  98. <option value="7">July</option>
  99. <option value="8">August</option>
  100. <option value="9">September</option>
  101. <option value="10">October</option>
  102. <option value="11">November</option>
  103. <option value="12">December</option>
  104. </select>
  105. <select name="mydate_day" id="id_mydate_day">
  106. <option value="">---</option>
  107. <option value="1">1</option>
  108. <option value="2">2</option>
  109. <option value="3">3</option>
  110. <option value="4">4</option>
  111. <option value="5">5</option>
  112. <option value="6">6</option>
  113. <option value="7">7</option>
  114. <option value="8">8</option>
  115. <option value="9">9</option>
  116. <option value="10">10</option>
  117. <option value="11">11</option>
  118. <option value="12">12</option>
  119. <option value="13">13</option>
  120. <option value="14">14</option>
  121. <option value="15" selected>15</option>
  122. <option value="16">16</option>
  123. <option value="17">17</option>
  124. <option value="18">18</option>
  125. <option value="19">19</option>
  126. <option value="20">20</option>
  127. <option value="21">21</option>
  128. <option value="22">22</option>
  129. <option value="23">23</option>
  130. <option value="24">24</option>
  131. <option value="25">25</option>
  132. <option value="26">26</option>
  133. <option value="27">27</option>
  134. <option value="28">28</option>
  135. <option value="29">29</option>
  136. <option value="30">30</option>
  137. <option value="31">31</option>
  138. </select>
  139. <select name="mydate_year" id="id_mydate_year">
  140. <option value="">---</option>
  141. <option value="2007">2007</option>
  142. <option value="2008">2008</option>
  143. <option value="2009">2009</option>
  144. <option value="2010" selected>2010</option>
  145. <option value="2011">2011</option>
  146. <option value="2012">2012</option>
  147. <option value="2013">2013</option>
  148. <option value="2014">2014</option>
  149. <option value="2015">2015</option>
  150. <option value="2016">2016</option>
  151. </select>
  152. """
  153. ))
  154. def test_render_datetime(self):
  155. self.assertHTMLEqual(
  156. self.widget.render('mydate', date(2010, 4, 15)),
  157. self.widget.render('mydate', '2010-04-15'),
  158. )
  159. def test_render_invalid_date(self):
  160. """
  161. Invalid dates should still render the failed date.
  162. """
  163. self.check_html(self.widget, 'mydate', '2010-02-31', html=(
  164. """
  165. <select name="mydate_month" id="id_mydate_month">
  166. <option value="">---</option>
  167. <option value="1">January</option>
  168. <option value="2" selected>February</option>
  169. <option value="3">March</option>
  170. <option value="4">April</option>
  171. <option value="5">May</option>
  172. <option value="6">June</option>
  173. <option value="7">July</option>
  174. <option value="8">August</option>
  175. <option value="9">September</option>
  176. <option value="10">October</option>
  177. <option value="11">November</option>
  178. <option value="12">December</option>
  179. </select>
  180. <select name="mydate_day" id="id_mydate_day">
  181. <option value="">---</option>
  182. <option value="1">1</option>
  183. <option value="2">2</option>
  184. <option value="3">3</option>
  185. <option value="4">4</option>
  186. <option value="5">5</option>
  187. <option value="6">6</option>
  188. <option value="7">7</option>
  189. <option value="8">8</option>
  190. <option value="9">9</option>
  191. <option value="10">10</option>
  192. <option value="11">11</option>
  193. <option value="12">12</option>
  194. <option value="13">13</option>
  195. <option value="14">14</option>
  196. <option value="15">15</option>
  197. <option value="16">16</option>
  198. <option value="17">17</option>
  199. <option value="18">18</option>
  200. <option value="19">19</option>
  201. <option value="20">20</option>
  202. <option value="21">21</option>
  203. <option value="22">22</option>
  204. <option value="23">23</option>
  205. <option value="24">24</option>
  206. <option value="25">25</option>
  207. <option value="26">26</option>
  208. <option value="27">27</option>
  209. <option value="28">28</option>
  210. <option value="29">29</option>
  211. <option value="30">30</option>
  212. <option value="31" selected>31</option>
  213. </select>
  214. <select name="mydate_year" id="id_mydate_year">
  215. <option value="">---</option>
  216. <option value="2007">2007</option>
  217. <option value="2008">2008</option>
  218. <option value="2009">2009</option>
  219. <option value="2010" selected>2010</option>
  220. <option value="2011">2011</option>
  221. <option value="2012">2012</option>
  222. <option value="2013">2013</option>
  223. <option value="2014">2014</option>
  224. <option value="2015">2015</option>
  225. <option value="2016">2016</option>
  226. </select>
  227. """
  228. ))
  229. def test_custom_months(self):
  230. widget = SelectDateWidget(months=MONTHS_AP, years=('2013',))
  231. self.check_html(widget, 'mydate', '', html=(
  232. """
  233. <select name="mydate_month" id="id_mydate_month">
  234. <option selected value="">---</option>
  235. <option value="1">Jan.</option>
  236. <option value="2">Feb.</option>
  237. <option value="3">March</option>
  238. <option value="4">April</option>
  239. <option value="5">May</option>
  240. <option value="6">June</option>
  241. <option value="7">July</option>
  242. <option value="8">Aug.</option>
  243. <option value="9">Sept.</option>
  244. <option value="10">Oct.</option>
  245. <option value="11">Nov.</option>
  246. <option value="12">Dec.</option>
  247. </select>
  248. <select name="mydate_day" id="id_mydate_day">
  249. <option selected value="">---</option>
  250. <option value="1">1</option>
  251. <option value="2">2</option>
  252. <option value="3">3</option>
  253. <option value="4">4</option>
  254. <option value="5">5</option>
  255. <option value="6">6</option>
  256. <option value="7">7</option>
  257. <option value="8">8</option>
  258. <option value="9">9</option>
  259. <option value="10">10</option>
  260. <option value="11">11</option>
  261. <option value="12">12</option>
  262. <option value="13">13</option>
  263. <option value="14">14</option>
  264. <option value="15">15</option>
  265. <option value="16">16</option>
  266. <option value="17">17</option>
  267. <option value="18">18</option>
  268. <option value="19">19</option>
  269. <option value="20">20</option>
  270. <option value="21">21</option>
  271. <option value="22">22</option>
  272. <option value="23">23</option>
  273. <option value="24">24</option>
  274. <option value="25">25</option>
  275. <option value="26">26</option>
  276. <option value="27">27</option>
  277. <option value="28">28</option>
  278. <option value="29">29</option>
  279. <option value="30">30</option>
  280. <option value="31">31</option>
  281. </select>
  282. <select name="mydate_year" id="id_mydate_year">
  283. <option selected value="">---</option>
  284. <option value="2013">2013</option>
  285. </select>
  286. """
  287. ))
  288. def test_selectdate_required(self):
  289. class GetNotRequiredDate(Form):
  290. mydate = DateField(widget=SelectDateWidget, required=False)
  291. class GetRequiredDate(Form):
  292. mydate = DateField(widget=SelectDateWidget, required=True)
  293. self.assertFalse(GetNotRequiredDate().fields['mydate'].widget.is_required)
  294. self.assertTrue(GetRequiredDate().fields['mydate'].widget.is_required)
  295. def test_selectdate_empty_label(self):
  296. w = SelectDateWidget(years=('2014',), empty_label='empty_label')
  297. # Rendering the default state with empty_label set as string.
  298. self.assertInHTML('<option selected value="">empty_label</option>', w.render('mydate', ''), count=3)
  299. w = SelectDateWidget(years=('2014',), empty_label=('empty_year', 'empty_month', 'empty_day'))
  300. # Rendering the default state with empty_label tuple.
  301. self.assertHTMLEqual(
  302. w.render('mydate', ''),
  303. """
  304. <select name="mydate_month" id="id_mydate_month">
  305. <option selected value="">empty_month</option>
  306. <option value="1">January</option>
  307. <option value="2">February</option>
  308. <option value="3">March</option>
  309. <option value="4">April</option>
  310. <option value="5">May</option>
  311. <option value="6">June</option>
  312. <option value="7">July</option>
  313. <option value="8">August</option>
  314. <option value="9">September</option>
  315. <option value="10">October</option>
  316. <option value="11">November</option>
  317. <option value="12">December</option>
  318. </select>
  319. <select name="mydate_day" id="id_mydate_day">
  320. <option selected value="">empty_day</option>
  321. <option value="1">1</option>
  322. <option value="2">2</option>
  323. <option value="3">3</option>
  324. <option value="4">4</option>
  325. <option value="5">5</option>
  326. <option value="6">6</option>
  327. <option value="7">7</option>
  328. <option value="8">8</option>
  329. <option value="9">9</option>
  330. <option value="10">10</option>
  331. <option value="11">11</option>
  332. <option value="12">12</option>
  333. <option value="13">13</option>
  334. <option value="14">14</option>
  335. <option value="15">15</option>
  336. <option value="16">16</option>
  337. <option value="17">17</option>
  338. <option value="18">18</option>
  339. <option value="19">19</option>
  340. <option value="20">20</option>
  341. <option value="21">21</option>
  342. <option value="22">22</option>
  343. <option value="23">23</option>
  344. <option value="24">24</option>
  345. <option value="25">25</option>
  346. <option value="26">26</option>
  347. <option value="27">27</option>
  348. <option value="28">28</option>
  349. <option value="29">29</option>
  350. <option value="30">30</option>
  351. <option value="31">31</option>
  352. </select>
  353. <select name="mydate_year" id="id_mydate_year">
  354. <option selected value="">empty_year</option>
  355. <option value="2014">2014</option>
  356. </select>
  357. """,
  358. )
  359. with self.assertRaisesMessage(ValueError, 'empty_label list/tuple must have 3 elements.'):
  360. SelectDateWidget(years=('2014',), empty_label=('not enough', 'values'))
  361. @override_settings(USE_L10N=True)
  362. @translation.override('nl')
  363. def test_l10n(self):
  364. w = SelectDateWidget(
  365. years=('2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016')
  366. )
  367. self.assertEqual(
  368. w.value_from_datadict({'date_year': '2010', 'date_month': '8', 'date_day': '13'}, {}, 'date'),
  369. '13-08-2010',
  370. )
  371. self.assertHTMLEqual(
  372. w.render('date', '13-08-2010'),
  373. """
  374. <select name="date_day" id="id_date_day">
  375. <option value="">---</option>
  376. <option value="1">1</option>
  377. <option value="2">2</option>
  378. <option value="3">3</option>
  379. <option value="4">4</option>
  380. <option value="5">5</option>
  381. <option value="6">6</option>
  382. <option value="7">7</option>
  383. <option value="8">8</option>
  384. <option value="9">9</option>
  385. <option value="10">10</option>
  386. <option value="11">11</option>
  387. <option value="12">12</option>
  388. <option value="13" selected>13</option>
  389. <option value="14">14</option>
  390. <option value="15">15</option>
  391. <option value="16">16</option>
  392. <option value="17">17</option>
  393. <option value="18">18</option>
  394. <option value="19">19</option>
  395. <option value="20">20</option>
  396. <option value="21">21</option>
  397. <option value="22">22</option>
  398. <option value="23">23</option>
  399. <option value="24">24</option>
  400. <option value="25">25</option>
  401. <option value="26">26</option>
  402. <option value="27">27</option>
  403. <option value="28">28</option>
  404. <option value="29">29</option>
  405. <option value="30">30</option>
  406. <option value="31">31</option>
  407. </select>
  408. <select name="date_month" id="id_date_month">
  409. <option value="">---</option>
  410. <option value="1">januari</option>
  411. <option value="2">februari</option>
  412. <option value="3">maart</option>
  413. <option value="4">april</option>
  414. <option value="5">mei</option>
  415. <option value="6">juni</option>
  416. <option value="7">juli</option>
  417. <option value="8" selected>augustus</option>
  418. <option value="9">september</option>
  419. <option value="10">oktober</option>
  420. <option value="11">november</option>
  421. <option value="12">december</option>
  422. </select>
  423. <select name="date_year" id="id_date_year">
  424. <option value="">---</option>
  425. <option value="2007">2007</option>
  426. <option value="2008">2008</option>
  427. <option value="2009">2009</option>
  428. <option value="2010" selected>2010</option>
  429. <option value="2011">2011</option>
  430. <option value="2012">2012</option>
  431. <option value="2013">2013</option>
  432. <option value="2014">2014</option>
  433. <option value="2015">2015</option>
  434. <option value="2016">2016</option>
  435. </select>
  436. """,
  437. )
  438. # Even with an invalid date, the widget should reflect the entered value (#17401).
  439. self.assertEqual(w.render('mydate', '2010-02-30').count('selected'), 3)
  440. # Years before 1900 should work.
  441. w = SelectDateWidget(years=('1899',))
  442. self.assertEqual(
  443. w.value_from_datadict({'date_year': '1899', 'date_month': '8', 'date_day': '13'}, {}, 'date'),
  444. '13-08-1899',
  445. )
  446. # And years before 1000 (demonstrating the need for datetime_safe).
  447. w = SelectDateWidget(years=('0001',))
  448. self.assertEqual(
  449. w.value_from_datadict({'date_year': '0001', 'date_month': '8', 'date_day': '13'}, {}, 'date'),
  450. '13-08-0001',
  451. )
  452. def test_format_value(self):
  453. valid_formats = [
  454. '2000-1-1', '2000-10-15', '2000-01-01',
  455. '2000-01-0', '2000-0-01', '2000-0-0',
  456. '0-01-01', '0-01-0', '0-0-01', '0-0-0',
  457. ]
  458. for value in valid_formats:
  459. year, month, day = (int(x) or '' for x in value.split('-'))
  460. with self.subTest(value=value):
  461. self.assertEqual(self.widget.format_value(value), {'day': day, 'month': month, 'year': year})
  462. invalid_formats = [
  463. '2000-01-001', '2000-001-01', '2-01-01', '20-01-01', '200-01-01',
  464. '20000-01-01',
  465. ]
  466. for value in invalid_formats:
  467. with self.subTest(value=value):
  468. self.assertEqual(self.widget.format_value(value), {'day': None, 'month': None, 'year': None})
  469. def test_value_from_datadict(self):
  470. tests = [
  471. (('2000', '12', '1'), '2000-12-1'),
  472. (('', '12', '1'), '0-12-1'),
  473. (('2000', '', '1'), '2000-0-1'),
  474. (('2000', '12', ''), '2000-12-0'),
  475. (('', '', '', ''), None),
  476. ((None, '12', '1'), None),
  477. (('2000', None, '1'), None),
  478. (('2000', '12', None), None),
  479. ]
  480. for values, expected in tests:
  481. with self.subTest(values=values):
  482. data = {}
  483. for field_name, value in zip(('year', 'month', 'day'), values):
  484. if value is not None:
  485. data['field_%s' % field_name] = value
  486. self.assertEqual(self.widget.value_from_datadict(data, {}, 'field'), expected)
  487. def test_value_omitted_from_data(self):
  488. self.assertIs(self.widget.value_omitted_from_data({}, {}, 'field'), True)
  489. self.assertIs(self.widget.value_omitted_from_data({'field_month': '12'}, {}, 'field'), False)
  490. self.assertIs(self.widget.value_omitted_from_data({'field_year': '2000'}, {}, 'field'), False)
  491. self.assertIs(self.widget.value_omitted_from_data({'field_day': '1'}, {}, 'field'), False)
  492. data = {'field_day': '1', 'field_month': '12', 'field_year': '2000'}
  493. self.assertIs(self.widget.value_omitted_from_data(data, {}, 'field'), False)
  494. @override_settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=True)
  495. def test_years_rendered_without_separator(self):
  496. widget = SelectDateWidget(years=(2007,))
  497. self.check_html(widget, 'mydate', '', html=(
  498. """
  499. <select name="mydate_month" id="id_mydate_month">
  500. <option selected value="">---</option>
  501. <option value="1">January</option>
  502. <option value="2">February</option>
  503. <option value="3">March</option>
  504. <option value="4">April</option>
  505. <option value="5">May</option>
  506. <option value="6">June</option>
  507. <option value="7">July</option>
  508. <option value="8">August</option>
  509. <option value="9">September</option>
  510. <option value="10">October</option>
  511. <option value="11">November</option>
  512. <option value="12">December</option>
  513. </select>
  514. <select name="mydate_day" id="id_mydate_day">
  515. <option selected value="">---</option>
  516. <option value="1">1</option>
  517. <option value="2">2</option>
  518. <option value="3">3</option>
  519. <option value="4">4</option>
  520. <option value="5">5</option>
  521. <option value="6">6</option>
  522. <option value="7">7</option>
  523. <option value="8">8</option>
  524. <option value="9">9</option>
  525. <option value="10">10</option>
  526. <option value="11">11</option>
  527. <option value="12">12</option>
  528. <option value="13">13</option>
  529. <option value="14">14</option>
  530. <option value="15">15</option>
  531. <option value="16">16</option>
  532. <option value="17">17</option>
  533. <option value="18">18</option>
  534. <option value="19">19</option>
  535. <option value="20">20</option>
  536. <option value="21">21</option>
  537. <option value="22">22</option>
  538. <option value="23">23</option>
  539. <option value="24">24</option>
  540. <option value="25">25</option>
  541. <option value="26">26</option>
  542. <option value="27">27</option>
  543. <option value="28">28</option>
  544. <option value="29">29</option>
  545. <option value="30">30</option>
  546. <option value="31">31</option>
  547. </select>
  548. <select name="mydate_year" id="id_mydate_year">
  549. <option selected value="">---</option>
  550. <option value="2007">2007</option>
  551. </select>
  552. """
  553. ))