Skip to content
Browse files

[soc2009/model-validation] Merged to trunk at r12070

  • Loading branch information...
1 parent a7c320f commit ffd5564a87c5c8e5d5e1e201ae3462807b955efc @HonzaKral HonzaKral committed Jan 4, 2010
Showing with 1,653 additions and 357 deletions.
  1. +6 −0 AUTHORS
  2. +1 −0 django/conf/global_settings.py
  3. +35 −12 django/conf/locale/it/formats.py
  4. +8 −8 django/conf/locale/no/formats.py
  5. +24 −25 django/conf/locale/pl/LC_MESSAGES/django.po
  6. +26 −20 django/contrib/admin/media/js/admin/DateTimeShortcuts.js
  7. +39 −0 django/contrib/admin/media/js/core.js
  8. +17 −15 django/contrib/admin/options.py
  9. +2 −2 django/contrib/admin/templatetags/admin_list.py
  10. +1 −2 django/contrib/admin/util.py
  11. +6 −6 django/contrib/admin/widgets.py
  12. +16 −11 django/contrib/auth/models.py
  13. +15 −0 django/contrib/auth/tests/auth_backends.py
  14. +1 −1 django/contrib/databrowse/datastructures.py
  15. +1 −1 django/contrib/databrowse/plugins/calendars.py
  16. +1 −1 django/contrib/databrowse/templates/databrowse/calendar_day.html
  17. +1 −1 django/contrib/databrowse/templates/databrowse/calendar_main.html
  18. +2 −2 django/contrib/databrowse/templates/databrowse/calendar_month.html
  19. +7 −7 django/contrib/gis/db/backends/postgis/operations.py
  20. +16 −13 django/contrib/gis/db/backends/spatialite/base.py
  21. +1 −2 django/contrib/gis/db/backends/spatialite/creation.py
  22. +9 −12 django/contrib/gis/db/backends/spatialite/operations.py
  23. +4 −1 django/contrib/gis/db/models/manager.py
  24. +2 −2 django/contrib/gis/db/models/sql/compiler.py
  25. +7 −2 django/contrib/gis/geos/prototypes/errcheck.py
  26. +0 −2 django/contrib/gis/tests/relatedapp/tests.py
  27. 0 django/contrib/localflavor/id/__init__.py
  28. +210 −0 django/contrib/localflavor/id/forms.py
  29. +101 −0 django/contrib/localflavor/id/id_choices.py
  30. 0 django/contrib/localflavor/ie/__init__.py
  31. +13 −0 django/contrib/localflavor/ie/forms.py
  32. +40 −0 django/contrib/localflavor/ie/ie_counties.py
  33. 0 django/contrib/localflavor/kw/__init__.py
  34. +61 −0 django/contrib/localflavor/kw/forms.py
  35. 0 django/contrib/localflavor/pt/__init__.py
  36. +47 −0 django/contrib/localflavor/pt/forms.py
  37. +1 −1 django/contrib/localflavor/uk/forms.py
  38. +2 −2 django/contrib/localflavor/us/forms.py
  39. +5 −11 django/contrib/localflavor/us/models.py
  40. 0 django/contrib/localflavor/uy/__init__.py
  41. +59 −0 django/contrib/localflavor/uy/forms.py
  42. +12 −0 django/contrib/localflavor/uy/util.py
  43. +24 −0 django/contrib/localflavor/uy/uy_departaments.py
  44. +3 −3 django/core/management/commands/test.py
  45. +8 −11 django/forms/widgets.py
  46. +7 −7 django/template/defaultfilters.py
  47. +36 −5 django/test/simple.py
  48. +7 −0 django/utils/datastructures.py
  49. +13 −1 django/utils/dateformat.py
  50. +13 −4 django/utils/encoding.py
  51. +36 −20 django/utils/formats.py
  52. +3 −0 django/utils/translation/trans_real.py
  53. +16 −9 django/views/i18n.py
  54. +95 −8 docs/ref/contrib/localflavor.txt
  55. +2 −4 docs/ref/django-admin.txt
  56. +1 −1 docs/ref/settings.txt
  57. +2 −0 docs/ref/templates/builtins.txt
  58. +14 −1 docs/releases/1.1.2.txt
  59. +16 −4 docs/releases/1.2.txt
  60. +1 −1 docs/topics/auth.txt
  61. +19 −2 docs/topics/testing.txt
  62. +10 −1 tests/regressiontests/admin_views/tests.py
  63. +9 −0 tests/regressiontests/admin_widgets/models.py
  64. +2 −1 tests/regressiontests/admin_widgets/tests.py
  65. +5 −2 tests/regressiontests/datastructures/tests.py
  66. +4 −0 tests/regressiontests/dateformat/tests.py
  67. +175 −0 tests/regressiontests/forms/localflavor/id.py
  68. +12 −0 tests/regressiontests/forms/localflavor/ie.py
  69. +15 −0 tests/regressiontests/forms/localflavor/kw.py
  70. +106 −0 tests/regressiontests/forms/localflavor/pt.py
  71. +11 −0 tests/regressiontests/forms/localflavor/uk.py
  72. +46 −0 tests/regressiontests/forms/localflavor/uy.py
  73. +10 −0 tests/regressiontests/forms/tests.py
  74. +36 −0 tests/regressiontests/forms/widgets.py
  75. +4 −0 tests/regressiontests/i18n/forms.py
  76. +5 −1 tests/regressiontests/i18n/models.py
  77. +59 −8 tests/regressiontests/i18n/tests.py
  78. +0 −7 tests/regressiontests/localflavor/forms.py
  79. +0 −8 tests/regressiontests/localflavor/models.py
  80. +0 −83 tests/regressiontests/localflavor/tests.py
  81. +4 −0 tests/regressiontests/settings/__init__.py
  82. BIN tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.mo
  83. +20 −0 tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.po
  84. +4 −2 tests/regressiontests/views/tests/i18n.py
  85. +1 −1 tests/runtests.py
View
6 AUTHORS
@@ -28,9 +28,11 @@ answer newbie questions, and generally made Django that much better:
ajs <adi@sieker.info>
alang@bright-green.com
+ Alcides Fonseca
Andi Albrecht <albrecht.andi@gmail.com>
Marty Alchin <gulopine@gamemusic.org>
Ahmad Alhashemi <trans@ahmadh.com>
+ Ahmad Al-Ibrahim
Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
AgarFu <heaven@croasanaso.sytes.net>
Dagur Páll Ammendrup <dagurp@gmail.com>
@@ -188,13 +190,15 @@ answer newbie questions, and generally made Django that much better:
Simon Greenhill <dev@simon.net.nz>
Owen Griffiths
Espen Grindhaug <http://grindhaug.org/>
+ Janos Guljas
Thomas Güttler <hv@tbz-pariv.de>
Horst Gutmann <zerok@zerokspot.com>
Scot Hacker <shacker@birdhouse.org>
dAniel hAhler
hambaloney
Brian Harring <ferringb@gmail.com>
Brant Harris
+ Ronny Haryanto <http://ronny.haryan.to/>
Hawkeye
Joe Heck <http://www.rhonabwy.com/wp/>
Joel Heenan <joelh-django@planetjoel.com>
@@ -384,6 +388,7 @@ answer newbie questions, and generally made Django that much better:
Rozza <ross.lawley@gmail.com>
Oliver Rutherfurd <http://rutherfurd.net/>
ryankanno
+ Gonzalo Saavedra <gonzalosaavedra@gmail.com>
Manuel Saelices <msaelices@yaco.es>
Ivan Sagalaev (Maniac) <http://www.softwaremaniacs.org/>
Vinay Sajip <vinay_sajip@yahoo.co.uk>
@@ -482,6 +487,7 @@ answer newbie questions, and generally made Django that much better:
ymasuda@ethercube.com
Jesse Young <adunar@gmail.com>
Mykola Zamkovoi <nickzam@gmail.com>
+ zegor
Gasper Zejn <zejn@kiberpipa.org>
Jarek Zgoda <jarek.zgoda@gmail.com>
Cheng Zhang
View
1 django/conf/global_settings.py
@@ -84,6 +84,7 @@
('sk', gettext_noop('Slovak')),
('sl', gettext_noop('Slovenian')),
('sr', gettext_noop('Serbian')),
+ ('sr-latn', gettext_noop('Serbian Latin')),
('sv', gettext_noop('Swedish')),
('ta', gettext_noop('Tamil')),
('te', gettext_noop('Telugu')),
View
47 django/conf/locale/it/formats.py
@@ -2,17 +2,40 @@
# This file is distributed under the same license as the Django package.
#
-DATE_FORMAT = 'd F Y'
-TIME_FORMAT = 'H.i.s'
-# DATETIME_FORMAT =
-YEAR_MONTH_FORMAT = 'F Y'
-MONTH_DAY_FORMAT = 'j F'
-SHORT_DATE_FORMAT = 'd/M/Y'
-# SHORT_DATETIME_FORMAT =
-# FIRST_DAY_OF_WEEK =
-# DATE_INPUT_FORMATS =
-# TIME_INPUT_FORMATS =
-# DATETIME_INPUT_FORMATS =
+DATE_FORMAT = 'd F Y' # 25 Ottobre 2006
+TIME_FORMAT = 'H:i:s' # 14:30:59
+DATETIME_FORMAT = 'w d F Y H:i:s' # Mercoledì 25 Ottobre 2006 14:30:59
+YEAR_MONTH_FORMAT = 'F Y' # Ottobre 2006
+MONTH_DAY_FORMAT = 'j/F' # 10/2006
+SHORT_DATE_FORMAT = 'd/M/Y' # 25/12/2009
+SHORT_DATETIME_FORMAT = 'd/M/Y H:i:s' # 25/10/2009 14:30:59
+FIRST_DAY_OF_WEEK = 1 # Lunedì
+DATE_INPUT_FORMATS = (
+ '%Y-%m-%d', '%Y/%m/%d', # '2008-10-25', '2008/10/25'
+ '%d-%m-%Y', '%d/%m/%Y', # '25-10-2006', '25/10/2006'
+ '%d-%m-%y', '%d/%m/%y', # '25-10-06', '25/10/06'
+)
+TIME_INPUT_FORMATS = (
+ '%H:%M:%S', # '14:30:59'
+ '%H:%M', # '14:30'
+)
+DATETIME_INPUT_FORMATS = (
+ '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
+ '%Y-%m-%d %H:%M', # '2006-10-25 14:30'
+ '%Y-%m-%d', # '2006-10-25'
+ '%d-%m-%Y %H:%M:%S', # '25-10-2006 14:30:59'
+ '%d-%m-%Y %H:%M', # '25-10-2006 14:30'
+ '%d-%m-%Y', # '25-10-2006'
+ '%d-%m-%y %H:%M:%S', # '25-10-06 14:30:59'
+ '%d-%m-%y %H:%M', # '25-10-06 14:30'
+ '%d-%m-%y', # '25-10-06'
+ '%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59'
+ '%d/%m/%Y %H:%M', # '25/10/2006 14:30'
+ '%d/%m/%Y', # '25/10/2006'
+ '%d/%m/%y %H:%M:%S', # '25/10/06 14:30:59'
+ '%d/%m/%y %H:%M', # '25/10/06 14:30'
+ '%d/%m/%y', # '25/10/06'
+)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = '.'
-# NUMBER_GROUPING =
+NUMBER_GROUPING = 3
View
16 django/conf/locale/no/formats.py
@@ -14,19 +14,19 @@
'%j. %B %Y', '%j %B %Y', # '25. oktober 2006', '25 oktober 2006'
)
TIME_INPUT_FORMATS = (
- '%H:%i:%S', # '14:30:59'
- '%H:%i', # '14:30'
+ '%H:%M:%S', # '14:30:59'
+ '%H:%M', # '14:30'
)
DATETIME_INPUT_FORMATS = (
- '%Y-%m-%d %H:%i:%S', # '2006-10-25 14:30:59'
- '%Y-%m-%d %H:%i', # '2006-10-25 14:30'
+ '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
+ '%Y-%m-%d %H:%M', # '2006-10-25 14:30'
'%Y-%m-%d', # '2006-10-25'
'%Y-%m-%j', # '2006-10-25'
- '%j.%m.%Y %H:%i:%S', # '25.10.2006 14:30:59'
- '%j.%m.%Y %H:%i', # '25.10.2006 14:30'
+ '%j.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59'
+ '%j.%m.%Y %H:%M', # '25.10.2006 14:30'
'%j.%m.%Y', # '25.10.2006'
- '%j.%m.%y %H:%i:%S', # '25.10.06 14:30:59'
- '%j.%m.%y %H:%i', # '25.10.06 14:30'
+ '%j.%m.%y %H:%M:%S', # '25.10.06 14:30:59'
+ '%j.%m.%y %H:%M', # '25.10.06 14:30'
'%j.%m.%y', # '25.10.06'
)
DECIMAL_SEPARATOR = ','
View
49 django/conf/locale/pl/LC_MESSAGES/django.po
@@ -5,7 +5,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-12-23 12:55+0100\n"
+"POT-Creation-Date: 2009-12-30 08:54+0100\n"
"PO-Revision-Date: 2008-02-25 15:53+0100\n"
"Last-Translator: Jarek Zgoda <jarek.zgoda@gmail.com>\n"
"MIME-Version: 1.0\n"
@@ -223,7 +223,7 @@ msgstr "chiński tradycyjny"
msgid "Successfully deleted %(count)d %(items)s."
msgstr "Usunięto %(count)d %(items)s."
-#: contrib/admin/actions.py:67 contrib/admin/options.py:1073
+#: contrib/admin/actions.py:67 contrib/admin/options.py:1075
msgid "Are you sure?"
msgstr "Jesteś pewien?"
@@ -314,107 +314,107 @@ msgstr "logi"
msgid "None"
msgstr "brak"
-#: contrib/admin/options.py:540
+#: contrib/admin/options.py:554
#, python-format
msgid "Changed %s."
msgstr "Zmieniono %s"
-#: contrib/admin/options.py:540 contrib/admin/options.py:550
+#: contrib/admin/options.py:554 contrib/admin/options.py:564
#: contrib/comments/templates/comments/preview.html:16 forms/models.py:385
#: forms/models.py:598
msgid "and"
msgstr "i"
-#: contrib/admin/options.py:545
+#: contrib/admin/options.py:559
#, python-format
msgid "Added %(name)s \"%(object)s\"."
msgstr "Dodano %(name)s \"%(object)s\"."
-#: contrib/admin/options.py:549
+#: contrib/admin/options.py:563
#, python-format
msgid "Changed %(list)s for %(name)s \"%(object)s\"."
msgstr "Zmieniono %(list)s w %(name)s \"%(object)s\"."
-#: contrib/admin/options.py:554
+#: contrib/admin/options.py:568
#, python-format
msgid "Deleted %(name)s \"%(object)s\"."
msgstr "Usunięto %(name)s \"%(object)s\"."
-#: contrib/admin/options.py:558
+#: contrib/admin/options.py:572
msgid "No fields changed."
msgstr "Żadne pole nie zmienione."
-#: contrib/admin/options.py:620 contrib/auth/admin.py:68
+#: contrib/admin/options.py:634 contrib/auth/admin.py:68
#, python-format
msgid "The %(name)s \"%(obj)s\" was added successfully."
msgstr "%(name)s \"%(obj)s\" dodany pomyślnie."
-#: contrib/admin/options.py:624 contrib/admin/options.py:657
+#: contrib/admin/options.py:638 contrib/admin/options.py:671
#: contrib/auth/admin.py:77
msgid "You may edit it again below."
msgstr "Możesz ponownie edytować wpis poniżej."
-#: contrib/admin/options.py:634 contrib/admin/options.py:667
+#: contrib/admin/options.py:648 contrib/admin/options.py:681
#, python-format
msgid "You may add another %s below."
msgstr "Możesz dodać nowy wpis %s poniżej."
-#: contrib/admin/options.py:655
+#: contrib/admin/options.py:669
#, python-format
msgid "The %(name)s \"%(obj)s\" was changed successfully."
msgstr "%(name)s \"%(obj)s\" zostało pomyślnie zmienione."
-#: contrib/admin/options.py:663
+#: contrib/admin/options.py:677
#, python-format
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr ""
"%(name)s \"%(obj)s\" dodane pomyślnie. Możesz edytować ponownie wpis poniżej."
-#: contrib/admin/options.py:714
+#: contrib/admin/options.py:728
msgid ""
"Items must be selected in order to perform actions on them. No items have "
"been changed."
msgstr ""
"Wykonanie akcji wymaga wybrania obiektów. Żaden obiekt nie został zmieniony."
-#: contrib/admin/options.py:728
+#: contrib/admin/options.py:742
msgid "No action selected."
msgstr "Nie wybrano akcji."
-#: contrib/admin/options.py:808
+#: contrib/admin/options.py:822
#, python-format
msgid "Add %s"
msgstr "Dodaj %s"
-#: contrib/admin/options.py:840 contrib/admin/options.py:1051
+#: contrib/admin/options.py:848 contrib/admin/options.py:1053
#, python-format
msgid "%(name)s object with primary key %(key)r does not exist."
msgstr "Obiekt %(name)s o kluczu głównym %(key)r nie istnieje."
-#: contrib/admin/options.py:905
+#: contrib/admin/options.py:913
#, python-format
msgid "Change %s"
msgstr "Zmień %s"
-#: contrib/admin/options.py:950
+#: contrib/admin/options.py:958
msgid "Database error"
msgstr "Błąd bazy danych"
-#: contrib/admin/options.py:986
+#: contrib/admin/options.py:994
#, python-format
msgid "%(count)s %(name)s was changed successfully."
msgid_plural "%(count)s %(name)s were changed successfully."
msgstr[0] "%(count)s %(name)s został pomyślnie zmieniony."
msgstr[1] "%(count)s %(name)s zostały pomyślnie zmienione."
msgstr[2] "%(count)s %(name)s zostało pomyślnie zmienionych."
-#: contrib/admin/options.py:1066
+#: contrib/admin/options.py:1068
#, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
msgstr "%(name)s \"%(obj)s\" usunięty pomyślnie."
-#: contrib/admin/options.py:1103
+#: contrib/admin/options.py:1105
#, python-format
msgid "Change history: %s"
msgstr "Historia zmian: %s"
@@ -546,9 +546,8 @@ msgid ""
"There's been an error. It's been reported to the site administrators via e-"
"mail and should be fixed shortly. Thanks for your patience."
msgstr ""
-"Wystąpił niespodziewany błąd. Raport został wysłany e-mailem "
-"administratorowi strony i powinien zostać wkrótce naprawiony. Dziękujemy za "
-"cierpliwość."
+"Wystąpił niespodziewany błąd. Został on zgłoszony e-mailem administratorowi "
+"strony i powinien zostać wkrótce naprawiony. Dziękujemy za cierpliwość."
#: contrib/admin/templates/admin/actions.html:4
msgid "Run the selected action"
View
46 django/contrib/admin/media/js/admin/DateTimeShortcuts.js
@@ -44,7 +44,7 @@ var DateTimeShortcuts = {
var shortcuts_span = document.createElement('span');
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
var now_link = document.createElement('a');
- now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());");
+ now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
now_link.appendChild(document.createTextNode(gettext('Now')));
var clock_link = document.createElement('a');
clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
@@ -80,10 +80,10 @@ var DateTimeShortcuts = {
quickElement('h2', clock_box, gettext('Choose a time'));
time_list = quickElement('ul', clock_box, '');
time_list.className = 'timelist';
- quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());")
- quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '00:00:00');")
- quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '06:00:00');")
- quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '12:00:00');")
+ quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
+ quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
+ quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
+ quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
cancel_p = quickElement('p', clock_box, '');
cancel_p.className = 'calendar-cancel';
@@ -195,20 +195,19 @@ var DateTimeShortcuts = {
openCalendar: function(num) {
var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
- var inp = DateTimeShortcuts.calendarInputs[num];
-
- // Determine if the current value in the input has a valid date.
- // If so, draw the calendar with that date's year and month.
- if (inp.value) {
- var date_parts = inp.value.split('-');
- var year = date_parts[0];
- var month = parseFloat(date_parts[1]);
- if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
- DateTimeShortcuts.calendars[num].drawDate(month, year);
- }
- }
+ var inp = DateTimeShortcuts.calendarInputs[num];
+
+ // Determine if the current value in the input has a valid date.
+ // If so, draw the calendar with that date's year and month.
+ if (inp.value) {
+ var date_parts = inp.value.split('-');
+ var year = date_parts[0];
+ var month = parseFloat(date_parts[1]);
+ if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
+ DateTimeShortcuts.calendars[num].drawDate(month, year);
+ }
+ }
-
// Recalculate the clockbox position
// is it left-to-right or right-to-left layout ?
if (getStyle(document.body,'direction')!='rtl') {
@@ -237,12 +236,19 @@ var DateTimeShortcuts = {
DateTimeShortcuts.calendars[num].drawNextMonth();
},
handleCalendarCallback: function(num) {
- return "function(y, m, d) { DateTimeShortcuts.calendarInputs["+num+"].value = y+'-'+(m<10?'0':'')+m+'-'+(d<10?'0':'')+d; document.getElementById(DateTimeShortcuts.calendarDivName1+"+num+").style.display='none';}";
+ format = gettext('DATE_INPUT_FORMATS');
+ // the format needs to be escaped a little
+ format = format.replace('\\', '\\\\');
+ format = format.replace('\r', '\\r');
+ format = format.replace('\n', '\\n');
+ format = format.replace('\t', '\\t');
+ format = format.replace("'", "\\'");
+ return "function(y, m, d) { DateTimeShortcuts.calendarInputs["+num+"].value = new Date(y, m-1, d).strftime('"+format+"');document.getElementById(DateTimeShortcuts.calendarDivName1+"+num+").style.display='none';}";
},
handleCalendarQuickLink: function(num, offset) {
var d = new Date();
d.setDate(d.getDate() + offset)
- DateTimeShortcuts.calendarInputs[num].value = d.getISODate();
+ DateTimeShortcuts.calendarInputs[num].value = d.strftime(gettext('DATE_INPUT_FORMATS'));
DateTimeShortcuts.dismissCalendar(num);
},
cancelEventPropagation: function(e) {
View
39 django/contrib/admin/media/js/core.js
@@ -115,6 +115,10 @@ Date.prototype.getCorrectYear = function() {
return (y < 38) ? y + 2000 : y + 1900;
}
+Date.prototype.getTwelveHours = function() {
+ return (this.getHours() <= 12) ? this.getHours() : 24 - this.getHours();
+}
+
Date.prototype.getTwoDigitMonth = function() {
return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1);
}
@@ -123,6 +127,10 @@ Date.prototype.getTwoDigitDate = function() {
return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();
}
+Date.prototype.getTwoDigitTwelveHour = function() {
+ return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours();
+}
+
Date.prototype.getTwoDigitHour = function() {
return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours();
}
@@ -147,6 +155,37 @@ Date.prototype.getHourMinuteSecond = function() {
return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond();
}
+Date.prototype.strftime = function(format) {
+ var fields = {
+ c: this.toString(),
+ d: this.getTwoDigitDate(),
+ H: this.getTwoDigitHour(),
+ I: this.getTwoDigitTwelveHour(),
+ m: this.getTwoDigitMonth(),
+ M: this.getTwoDigitMinute(),
+ p: (this.getHours() >= 12) ? 'PM' : 'AM',
+ S: this.getTwoDigitSecond(),
+ w: '0' + this.getDay(),
+ x: this.toLocaleDateString(),
+ X: this.toLocaleTimeString(),
+ y: ('' + this.getFullYear()).substr(2, 4),
+ Y: '' + this.getFullYear(),
+ '%' : '%'
+ };
+ var result = '', i = 0;
+ while (i < format.length) {
+ if (format[i] === '%') {
+ result = result + fields[format[i + 1]];
+ ++i;
+ }
+ else {
+ result = result + format[i];
+ }
+ ++i;
+ }
+ return result;
+}
+
// ----------------------------------------------------------------------------
// String object extensions
// ----------------------------------------------------------------------------
View
32 django/contrib/admin/options.py
@@ -8,7 +8,7 @@
from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict
from django.contrib import messages
from django.views.decorators.csrf import csrf_protect
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import PermissionDenied, ValidationError
from django.db import models, transaction
from django.db.models.fields import BLANK_CHOICE_DASH
from django.http import Http404, HttpResponse, HttpResponseRedirect
@@ -368,6 +368,20 @@ def get_changelist(self, request, **kwargs):
from django.contrib.admin.views.main import ChangeList
return ChangeList
+ def get_object(self, request, object_id):
+ """
+ Returns an instance matching the primary key provided. ``None`` is
+ returned if no match is found (or the object_id failed validation
+ against the primary key field).
+ """
+ queryset = self.queryset(request)
+ model = queryset.model
+ try:
+ object_id = model._meta.pk.to_python(object_id)
+ return queryset.get(pk=object_id)
+ except (model.DoesNotExist, ValidationError):
+ return None
+
def get_changelist_form(self, request, **kwargs):
"""
Returns a Form class for use in the Formset on the changelist page.
@@ -829,13 +843,7 @@ def change_view(self, request, object_id, extra_context=None):
model = self.model
opts = model._meta
- try:
- obj = self.queryset(request).get(pk=unquote(object_id))
- except model.DoesNotExist:
- # Don't raise Http404 just yet, because we haven't checked
- # permissions yet. We don't want an unauthenticated user to be able
- # to determine whether a given object exists.
- obj = None
+ obj = self.get_object(request, unquote(object_id))
if not self.has_change_permission(request, obj):
raise PermissionDenied
@@ -1040,13 +1048,7 @@ def delete_view(self, request, object_id, extra_context=None):
opts = self.model._meta
app_label = opts.app_label
- try:
- obj = self.queryset(request).get(pk=unquote(object_id))
- except self.model.DoesNotExist:
- # Don't raise Http404 just yet, because we haven't checked
- # permissions yet. We don't want an unauthenticated user to be able
- # to determine whether a given object exists.
- obj = None
+ obj = self.get_object(request, unquote(object_id))
if not self.has_delete_permission(request, obj):
raise PermissionDenied
View
4 django/contrib/admin/templatetags/admin_list.py
@@ -247,8 +247,8 @@ def date_hierarchy(cl):
return {
'show': True,
'choices': [{
- 'link': link({year_field: year.year}),
- 'title': year.year
+ 'link': link({year_field: str(year.year)}),
+ 'title': str(year.year),
} for year in years]
}
date_hierarchy = register.inclusion_tag('admin/date_hierarchy.html')(date_hierarchy)
View
3 django/contrib/admin/util.py
@@ -252,8 +252,7 @@ def lookup_field(name, obj, model_admin=None):
def label_for_field(name, model, model_admin):
try:
- model._meta.get_field_by_name(name)[0]
- return name
+ return model._meta.get_field_by_name(name)[0].verbose_name
except models.FieldDoesNotExist:
if name == "__unicode__":
return force_unicode(model._meta.verbose_name)
View
12 django/contrib/admin/widgets.py
@@ -41,21 +41,21 @@ def render(self, name, value, attrs=None, choices=()):
(name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
return mark_safe(u''.join(output))
-class AdminDateWidget(forms.TextInput):
+class AdminDateWidget(forms.DateTimeInput):
class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
- def __init__(self, attrs={}):
- super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
+ def __init__(self, attrs={}, format=None):
+ super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'}, format=format)
-class AdminTimeWidget(forms.TextInput):
+class AdminTimeWidget(forms.TimeInput):
class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
- def __init__(self, attrs={}):
- super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'})
+ def __init__(self, attrs={}, format=None):
+ super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'}, format=format)
class AdminSplitDateTime(forms.SplitDateTimeWidget):
"""
View
27 django/contrib/auth/models.py
@@ -218,22 +218,26 @@ def get_group_permissions(self, obj=None):
permissions = set()
for backend in auth.get_backends():
if hasattr(backend, "get_group_permissions"):
- if obj is not None and backend.supports_object_permissions:
- group_permissions = backend.get_group_permissions(self, obj)
+ if obj is not None:
+ if backend.supports_object_permissions:
+ permissions.update(
+ backend.get_group_permissions(self, obj)
+ )
else:
- group_permissions = backend.get_group_permissions(self)
- permissions.update(group_permissions)
+ permissions.update(backend.get_group_permissions(self))
return permissions
def get_all_permissions(self, obj=None):
permissions = set()
for backend in auth.get_backends():
if hasattr(backend, "get_all_permissions"):
- if obj is not None and backend.supports_object_permissions:
- all_permissions = backend.get_all_permissions(self, obj)
+ if obj is not None:
+ if backend.supports_object_permissions:
+ permissions.update(
+ backend.get_all_permissions(self, obj)
+ )
else:
- all_permissions = backend.get_all_permissions(self)
- permissions.update(all_permissions)
+ permissions.update(backend.get_all_permissions(self))
return permissions
def has_perm(self, perm, obj=None):
@@ -255,9 +259,10 @@ def has_perm(self, perm, obj=None):
# Otherwise we need to check the backends.
for backend in auth.get_backends():
if hasattr(backend, "has_perm"):
- if obj is not None and backend.supports_object_permissions:
- if backend.has_perm(self, perm, obj):
- return True
+ if obj is not None:
+ if (backend.supports_object_permissions and
+ backend.has_perm(self, perm, obj)):
+ return True
else:
if backend.has_perm(self, perm):
return True
View
15 django/contrib/auth/tests/auth_backends.py
@@ -69,6 +69,21 @@ def test_custom_perms(self):
self.assertEqual(user.has_perm('test'), False)
self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), False)
+ def test_has_no_object_perm(self):
+ """Regressiontest for #12462"""
+ user = User.objects.get(username='test')
+ content_type=ContentType.objects.get_for_model(Group)
+ perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
+ user.user_permissions.add(perm)
+ user.save()
+
+ self.assertEqual(user.has_perm('auth.test', 'object'), False)
+ self.assertEqual(user.get_all_permissions('object'), set([]))
+ self.assertEqual(user.has_perm('auth.test'), True)
+ self.assertEqual(user.get_all_permissions(), set(['auth.test']))
+
+
+
class TestObj(object):
pass
View
2 django/contrib/databrowse/datastructures.py
@@ -158,7 +158,7 @@ def values(self):
if isinstance(self.field, models.DateTimeField):
objs = capfirst(formats.date_format(self.raw_value, 'DATETIME_FORMAT'))
elif isinstance(self.field, models.TimeField):
- objs = capfirst(formats.date_format(self.raw_value, 'TIME_FORMAT'))
+ objs = capfirst(formats.time_format(self.raw_value, 'TIME_FORMAT'))
else:
objs = capfirst(formats.date_format(self.raw_value, 'DATE_FORMAT'))
else:
View
2 django/contrib/databrowse/plugins/calendars.py
@@ -37,7 +37,7 @@ def urls(self, plugin_name, easy_instance_field):
return [mark_safe(u'%s%s/%s/%s/%s/%s/' % (
easy_instance_field.model.url(),
plugin_name, easy_instance_field.field.name,
- d.year,
+ str(d.year),
datetime_safe.new_date(d).strftime('%b').lower(),
d.day))]
View
2 django/contrib/databrowse/templates/databrowse/calendar_day.html
@@ -4,7 +4,7 @@
{% block content %}
-<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../../">Calendars</a> / <a href="../../../">By {{ field.verbose_name }}</a> / <a href="../../">{{ day.year }}</a> / <a href="../">{{ day|date:"F" }}</a> / {{ day.day }}</div>
+<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../../">Calendars</a> / <a href="../../../">By {{ field.verbose_name }}</a> / <a href="../../">{{ day|date:"Y" }}</a> / <a href="../">{{ day|date:"F" }}</a> / {{ day|date:"d" }}</div>
<h1>{{ object_list.count }} {% if object_list.count|pluralize %}{{ model.verbose_name_plural }}{% else %}{{ model.verbose_name }}{% endif %} with {{ field.verbose_name }} on {{ day|date:"F j, Y" }}</h1>
View
2 django/contrib/databrowse/templates/databrowse/calendar_main.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for year in date_list %}
-<li class="{% cycle 'odd' 'even' %}"><a href="{{ year.year }}/">{{ year.year }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ year|date:"Y" }}/">{{ year|date:"Y" }}</a></li>
{% endfor %}
</ul>
View
4 django/contrib/databrowse/templates/databrowse/calendar_month.html
@@ -4,9 +4,9 @@
{% block content %}
-<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../">Calendars</a> / <a href="../../">By {{ field.verbose_name }}</a> / <a href="../">{{ month.year }}</a> / {{ month|date:"F" }}</div>
+<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../">Calendars</a> / <a href="../../">By {{ field.verbose_name }}</a> / <a href="../">{{ month|date:"Y" }}</a> / {{ month|date:"F" }}</div>
-<h1>{{ object_list.count }} {% if object_list.count|pluralize %}{{ model.verbose_name_plural }}{% else %}{{ model.verbose_name }}{% endif %} with {{ field.verbose_name }} on {{ day|date:"F Y" }}</h1>
+<h1>{{ object_list.count }} {% if object_list.count|pluralize %}{{ model.verbose_name_plural }}{% else %}{{ model.verbose_name }}{% endif %} with {{ field.verbose_name }} on {{ month|date:"F Y" }}</h1>
<ul class="objectlist">
{% for object in object_list %}
View
14 django/contrib/gis/db/backends/postgis/operations.py
@@ -8,8 +8,7 @@
from django.contrib.gis.geometry.backend import Geometry
from django.contrib.gis.measure import Distance
from django.core.exceptions import ImproperlyConfigured
-from django.db.backends.postgresql.operations import DatabaseOperations
-from django.db.backends.postgresql_psycopg2.base import Database
+from django.db.backends.postgresql_psycopg2.base import Database, DatabaseOperations
#### Classes used in constructing PostGIS spatial SQL ####
class PostGISOperator(SpatialOperation):
@@ -404,11 +403,12 @@ def _get_postgis_func(self, func):
"""
cursor = self.connection._cursor()
try:
- cursor.execute('SELECT %s()' % func)
- row = cursor.fetchone()
- except:
- # Responsibility of callers to perform error handling.
- raise
+ try:
+ cursor.execute('SELECT %s()' % func)
+ row = cursor.fetchone()
+ except:
+ # Responsibility of callers to perform error handling.
+ raise
finally:
cursor.close()
return row[0]
View
29 django/contrib/gis/db/backends/spatialite/base.py
@@ -9,7 +9,6 @@
from django.contrib.gis.db.backends.spatialite.creation import SpatiaLiteCreation
from django.contrib.gis.db.backends.spatialite.operations import SpatiaLiteOperations
-
class DatabaseWrapper(SqliteDatabaseWrapper):
def __init__(self, *args, **kwargs):
# Before we get too far, make sure pysqlite 2.5+ is installed.
@@ -36,6 +35,7 @@ def __init__(self, *args, **kwargs):
def _cursor(self):
if self.connection is None:
+ ## The following is the same as in django.db.backends.sqlite3.base ##
settings_dict = self.settings_dict
if not settings_dict['NAME']:
from django.core.exceptions import ImproperlyConfigured
@@ -50,7 +50,11 @@ def _cursor(self):
self.connection.create_function("django_extract", 2, _sqlite_extract)
self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
self.connection.create_function("regexp", 2, _sqlite_regexp)
+ connection_created.send(sender=self.__class__)
+
+ ## From here on, customized for GeoDjango ##
+ # Enabling extension loading on the SQLite connection.
try:
self.connection.enable_load_extension(True)
except AttributeError:
@@ -59,15 +63,14 @@ def _cursor(self):
'the loading of extensions to use SpatiaLite.'
)
- connection_created.send(sender=self.__class__)
- return self.connection.cursor(factory=SQLiteCursorWrapper)
-
- def load_spatialite(self):
- """
- Loads the SpatiaLite library.
- """
- try:
- self._cursor().execute("SELECT load_extension(%s)", (self.spatialite_lib,))
- except Exception, msg:
- raise ImproperlyConfigured('Unable to load the SpatiaLite extension '
- '"%s" because: %s' % (self.spatialite_lib, msg))
+ # Loading the SpatiaLite library extension on the connection, and returning
+ # the created cursor.
+ cur = self.connection.cursor(factory=SQLiteCursorWrapper)
+ try:
+ cur.execute("SELECT load_extension(%s)", (self.spatialite_lib,))
+ except Exception, msg:
+ raise ImproperlyConfigured('Unable to load the SpatiaLite library extension '
+ '"%s" because: %s' % (self.spatialite_lib, msg))
+ return cur
+ else:
+ return self.connection.cursor(factory=SQLiteCursorWrapper)
View
3 django/contrib/gis/db/backends/spatialite/creation.py
@@ -24,8 +24,7 @@ def create_test_db(self, verbosity=1, autoclobber=False):
self.connection.settings_dict["NAME"] = test_database_name
can_rollback = self._rollback_works()
self.connection.settings_dict["SUPPORTS_TRANSACTIONS"] = can_rollback
- # Need to load the SpatiaLite library and initializatin SQL before running `syncdb`.
- self.connection.load_spatialite()
+ # Need to load the SpatiaLite initialization SQL before running `syncdb`.
self.load_spatialite_sql()
call_command('syncdb', verbosity=verbosity, interactive=False, database=self.connection.alias)
View
21 django/contrib/gis/db/backends/spatialite/operations.py
@@ -51,8 +51,7 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
name = 'spatialite'
spatialite = True
version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
- valid_aggregates = dict([(k, None) for k in
- ('Extent', 'Union')])
+ valid_aggregates = dict([(k, None) for k in ('Extent', 'Union')])
Adapter = SpatiaLiteAdapter
@@ -112,10 +111,7 @@ def __init__(self, connection):
super(DatabaseOperations, self).__init__()
self.connection = connection
- # Load the spatialite library (must be done before getting the
- # SpatiaLite version).
- self.connection.load_spatialite()
-
+ # Determine the version of the SpatiaLite library.
try:
vtup = self.spatialite_version_tuple()
version = vtup[1:]
@@ -211,11 +207,12 @@ def _get_spatialite_func(self, func):
"""
cursor = self.connection._cursor()
try:
- cursor.execute('SELECT %s()' % func)
- row = cursor.fetchone()
- except:
- # TODO: raise helpful exception here.
- raise
+ try:
+ cursor.execute('SELECT %s()' % func)
+ row = cursor.fetchone()
+ except:
+ # Responsibility of caller to perform error handling.
+ raise
finally:
cursor.close()
return row[0]
@@ -268,7 +265,7 @@ def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn):
"""
Returns the SpatiaLite-specific SQL for the given lookup value
[a tuple of (alias, column, db_type)], lookup type, lookup
- value, and the model field.
+ value, the model field, and the quoting function.
"""
alias, col, db_type = lvalue
View
5 django/contrib/gis/db/models/manager.py
@@ -10,7 +10,10 @@ class GeoManager(Manager):
use_for_related_fields = True
def get_query_set(self):
- return GeoQuerySet(model=self.model)
+ qs = GeoQuerySet(self.model)
+ if self._db is not None:
+ qs = qs.using(self._db)
+ return qs
def area(self, *args, **kwargs):
return self.get_query_set().area(*args, **kwargs)
View
4 django/contrib/gis/db/models/sql/compiler.py
@@ -191,8 +191,8 @@ def resolve_columns(self, row, fields=()):
if self.connection.ops.oracle or getattr(self.query, 'geo_values', False):
# We resolve the rest of the columns if we're on Oracle or if
# the `geo_values` attribute is defined.
- for value, field in izip(row[index_start:], fields):
- values.append(self.query.convert_values(value, field, self.connection))
+ for value, field in map(None, row[index_start:], fields):
+ values.append(self.query.convert_values(value, field, connection=self.connection))
else:
values.extend(row[index_start:])
return tuple(values)
View
9 django/contrib/gis/geos/prototypes/errcheck.py
@@ -1,6 +1,7 @@
"""
Error checking functions for GEOS ctypes prototype functions.
"""
+import os
from ctypes import c_void_p, string_at, CDLL
from django.contrib.gis.geos.error import GEOSException
from django.contrib.gis.geos.libgeos import lgeos, GEOS_VERSION
@@ -15,8 +16,12 @@
free.restype = None
else:
# Getting the `free` routine from the C library of the platform.
- # The C library is obtained by passing None into `CDLL`.
- libc = CDLL(None)
+ if os.name == 'nt':
+ # On NT, use the MS C library.
+ libc = CDLL('msvcrt')
+ else:
+ # On POSIX platforms C library is obtained by passing None into `CDLL`.
+ libc = CDLL(None)
free = libc.free
### ctypes error checking routines ###
View
2 django/contrib/gis/tests/relatedapp/tests.py
@@ -19,7 +19,6 @@ def test01_setup(self):
loc = Location.objects.create(point=Point(lon, lat))
c = City.objects.create(name=name, state=state, location=loc)
- @no_oracle # TODO: Fix select_related() problems w/Oracle and pagination.
def test02_select_related(self):
"Testing `select_related` on geographic models (see #7126)."
qs1 = City.objects.all()
@@ -34,7 +33,6 @@ def test02_select_related(self):
self.assertEqual(Point(lon, lat), c.location.point)
@no_mysql
- @no_oracle # Pagination problem is implicated in this test as well.
def test03_transform_related(self):
"Testing the `transform` GeoQuerySet method on related geographic models."
# All the transformations are to state plane coordinate systems using
View
0 django/contrib/localflavor/id/__init__.py
No changes.
View
210 django/contrib/localflavor/id/forms.py
@@ -0,0 +1,210 @@
+"""
+ID-specific Form helpers
+"""
+
+import re
+import time
+
+from django.forms import ValidationError
+from django.forms.fields import Field, Select, EMPTY_VALUES
+from django.utils.translation import ugettext_lazy as _
+from django.utils.encoding import smart_unicode
+
+postcode_re = re.compile(r'^[1-9]\d{4}$')
+phone_re = re.compile(r'^(\+62|0)[2-9]\d{7,10}$')
+plate_re = re.compile(r'^(?P<prefix>[A-Z]{1,2}) ' + \
+ r'(?P<number>\d{1,5})( (?P<suffix>([A-Z]{1,3}|[1-9][0-9]{,2})))?$')
+nik_re = re.compile(r'^\d{16}$')
+
+
+class IDPostCodeField(Field):
+ """
+ An Indonesian post code field.
+
+ http://id.wikipedia.org/wiki/Kode_pos
+ """
+ default_error_messages = {
+ 'invalid': _('Enter a valid post code'),
+ }
+
+ def clean(self, value):
+ super(IDPostCodeField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ value = value.strip()
+ if not postcode_re.search(value):
+ raise ValidationError(self.error_messages['invalid'])
+
+ if int(value) < 10110:
+ raise ValidationError(self.error_messages['invalid'])
+
+ # 1xxx0
+ if value[0] == '1' and value[4] != '0':
+ raise ValidationError(self.error_messages['invalid'])
+
+ return u'%s' % (value, )
+
+
+class IDProvinceSelect(Select):
+ """
+ A Select widget that uses a list of provinces of Indonesia as its
+ choices.
+ """
+
+ def __init__(self, attrs=None):
+ from id_choices import PROVINCE_CHOICES
+ super(IDProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
+
+
+class IDPhoneNumberField(Field):
+ """
+ An Indonesian telephone number field.
+
+ http://id.wikipedia.org/wiki/Daftar_kode_telepon_di_Indonesia
+ """
+ default_error_messages = {
+ 'invalid': _('Enter a valid phone number'),
+ }
+
+ def clean(self, value):
+ super(IDPhoneNumberField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ phone_number = re.sub(r'[\-\s\(\)]', '', smart_unicode(value))
+
+ if phone_re.search(phone_number):
+ return smart_unicode(value)
+
+ raise ValidationError(self.error_messages['invalid'])
+
+
+class IDLicensePlatePrefixSelect(Select):
+ """
+ A Select widget that uses a list of vehicle license plate prefix code
+ of Indonesia as its choices.
+
+ http://id.wikipedia.org/wiki/Tanda_Nomor_Kendaraan_Bermotor
+ """
+
+ def __init__(self, attrs=None):
+ from id_choices import LICENSE_PLATE_PREFIX_CHOICES
+ super(IDLicensePlatePrefixSelect, self).__init__(attrs,
+ choices=LICENSE_PLATE_PREFIX_CHOICES)
+
+
+class IDLicensePlateField(Field):
+ """
+ An Indonesian vehicle license plate field.
+
+ http://id.wikipedia.org/wiki/Tanda_Nomor_Kendaraan_Bermotor
+
+ Plus: "B 12345 12"
+ """
+ default_error_messages = {
+ 'invalid': _('Enter a valid vehicle license plate number'),
+ }
+
+ def clean(self, value):
+ super(IDLicensePlateField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ plate_number = re.sub(r'\s+', ' ',
+ smart_unicode(value.strip())).upper()
+
+ matches = plate_re.search(plate_number)
+ if matches is None:
+ raise ValidationError(self.error_messages['invalid'])
+
+ # Make sure prefix is in the list of known codes.
+ from id_choices import LICENSE_PLATE_PREFIX_CHOICES
+ prefix = matches.group('prefix')
+ if prefix not in [choice[0] for choice in LICENSE_PLATE_PREFIX_CHOICES]:
+ raise ValidationError(self.error_messages['invalid'])
+
+ # Only Jakarta (prefix B) can have 3 letter suffix.
+ suffix = matches.group('suffix')
+ if suffix is not None and len(suffix) == 3 and prefix != 'B':
+ raise ValidationError(self.error_messages['invalid'])
+
+ # RI plates don't have suffix.
+ if prefix == 'RI' and suffix is not None and suffix != '':
+ raise ValidationError(self.error_messages['invalid'])
+
+ # Number can't be zero.
+ number = matches.group('number')
+ if number == '0':
+ raise ValidationError(self.error_messages['invalid'])
+
+ # CD, CC and B 12345 12
+ if len(number) == 5 or prefix in ('CD', 'CC'):
+ # suffix must be numeric and non-empty
+ if re.match(r'^\d+$', suffix) is None:
+ raise ValidationError(self.error_messages['invalid'])
+
+ # Known codes range is 12-124
+ if prefix in ('CD', 'CC') and not (12 <= int(number) <= 124):
+ raise ValidationError(self.error_messages['invalid'])
+ if len(number) == 5 and not (12 <= int(suffix) <= 124):
+ raise ValidationError(self.error_messages['invalid'])
+ else:
+ # suffix must be non-numeric
+ if suffix is not None and re.match(r'^[A-Z]{,3}$', suffix) is None:
+ raise ValidationError(self.error_messages['invalid'])
+
+ return plate_number
+
+
+class IDNationalIdentityNumberField(Field):
+ """
+ An Indonesian national identity number (NIK/KTP#) field.
+
+ http://id.wikipedia.org/wiki/Nomor_Induk_Kependudukan
+
+ xx.xxxx.ddmmyy.xxxx - 16 digits (excl. dots)
+ """
+ default_error_messages = {
+ 'invalid': _('Enter a valid NIK/KTP number'),
+ }
+
+ def clean(self, value):
+ super(IDNationalIdentityNumberField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ value = re.sub(r'[\s.]', '', smart_unicode(value))
+
+ if not nik_re.search(value):
+ raise ValidationError(self.error_messages['invalid'])
+
+ if int(value) == 0:
+ raise ValidationError(self.error_messages['invalid'])
+
+ def valid_nik_date(year, month, day):
+ try:
+ t1 = (int(year), int(month), int(day), 0, 0, 0, 0, 0, -1)
+ d = time.mktime(t1)
+ t2 = time.localtime(d)
+ if t1[:3] != t2[:3]:
+ return False
+ else:
+ return True
+ except (OverflowError, ValueError):
+ return False
+
+ year = int(value[10:12])
+ month = int(value[8:10])
+ day = int(value[6:8])
+ current_year = time.localtime().tm_year
+ if year < int(str(current_year)[-2:]):
+ if not valid_nik_date(2000 + int(year), month, day):
+ raise ValidationError(self.error_messages['invalid'])
+ elif not valid_nik_date(1900 + int(year), month, day):
+ raise ValidationError(self.error_messages['invalid'])
+
+ if value[:6] == '000000' or value[12:] == '0000':
+ raise ValidationError(self.error_messages['invalid'])
+
+ return '%s.%s.%s.%s' % (value[:2], value[2:6], value[6:12], value[12:])
View
101 django/contrib/localflavor/id/id_choices.py
@@ -0,0 +1,101 @@
+from django.utils.translation import ugettext_lazy as _
+
+# Reference: http://id.wikipedia.org/wiki/Daftar_provinsi_Indonesia
+
+# Indonesia does not have an official Province code standard.
+# I decided to use unambiguous and consistent (some are common) 3-letter codes.
+
+PROVINCE_CHOICES = (
+ ('BLI', _('Bali')),
+ ('BTN', _('Banten')),
+ ('BKL', _('Bengkulu')),
+ ('DIY', _('Yogyakarta')),
+ ('JKT', _('Jakarta')),
+ ('GOR', _('Gorontalo')),
+ ('JMB', _('Jambi')),
+ ('JBR', _('Jawa Barat')),
+ ('JTG', _('Jawa Tengah')),
+ ('JTM', _('Jawa Timur')),
+ ('KBR', _('Kalimantan Barat')),
+ ('KSL', _('Kalimantan Selatan')),
+ ('KTG', _('Kalimantan Tengah')),
+ ('KTM', _('Kalimantan Timur')),
+ ('BBL', _('Kepulauan Bangka-Belitung')),
+ ('KRI', _('Kepulauan Riau')),
+ ('LPG', _('Lampung')),
+ ('MLK', _('Maluku')),
+ ('MUT', _('Maluku Utara')),
+ ('NAD', _('Nanggroe Aceh Darussalam')),
+ ('NTB', _('Nusa Tenggara Barat')),
+ ('NTT', _('Nusa Tenggara Timur')),
+ ('PPA', _('Papua')),
+ ('PPB', _('Papua Barat')),
+ ('RIU', _('Riau')),
+ ('SLB', _('Sulawesi Barat')),
+ ('SLS', _('Sulawesi Selatan')),
+ ('SLT', _('Sulawesi Tengah')),
+ ('SLR', _('Sulawesi Tenggara')),
+ ('SLU', _('Sulawesi Utara')),
+ ('SMB', _('Sumatera Barat')),
+ ('SMS', _('Sumatera Selatan')),
+ ('SMU', _('Sumatera Utara')),
+)
+
+LICENSE_PLATE_PREFIX_CHOICES = (
+ ('A', _('Banten')),
+ ('AA', _('Magelang')),
+ ('AB', _('Yogyakarta')),
+ ('AD', _('Surakarta - Solo')),
+ ('AE', _('Madiun')),
+ ('AG', _('Kediri')),
+ ('B', _('Jakarta')),
+ ('BA', _('Sumatera Barat')),
+ ('BB', _('Tapanuli')),
+ ('BD', _('Bengkulu')),
+ ('BE', _('Lampung')),
+ ('BG', _('Sumatera Selatan')),
+ ('BH', _('Jambi')),
+ ('BK', _('Sumatera Utara')),
+ ('BL', _('Nanggroe Aceh Darussalam')),
+ ('BM', _('Riau')),
+ ('BN', _('Kepulauan Bangka Belitung')),
+ ('BP', _('Kepulauan Riau')),
+ ('CC', _('Corps Consulate')),
+ ('CD', _('Corps Diplomatic')),
+ ('D', _('Bandung')),
+ ('DA', _('Kalimantan Selatan')),
+ ('DB', _('Sulawesi Utara Daratan')),
+ ('DC', _('Sulawesi Barat')),
+ ('DD', _('Sulawesi Selatan')),
+ ('DE', _('Maluku')),
+ ('DG', _('Maluku Utara')),
+ ('DH', _('NTT - Timor')),
+ ('DK', _('Bali')),
+ ('DL', _('Sulawesi Utara Kepulauan')),
+ ('DM', _('Gorontalo')),
+ ('DN', _('Sulawesi Tengah')),
+ ('DR', _('NTB - Lombok')),
+ ('DS', _('Papua dan Papua Barat')),
+ ('DT', _('Sulawesi Tenggara')),
+ ('E', _('Cirebon')),
+ ('EA', _('NTB - Sumbawa')),
+ ('EB', _('NTT - Flores')),
+ ('ED', _('NTT - Sumba')),
+ ('F', _('Bogor')),
+ ('G', _('Pekalongan')),
+ ('H', _('Semarang')),
+ ('K', _('Pati')),
+ ('KB', _('Kalimantan Barat')),
+ ('KH', _('Kalimantan Tengah')),
+ ('KT', _('Kalimantan Timur')),
+ ('L', _('Surabaya')),
+ ('M', _('Madura')),
+ ('N', _('Malang')),
+ ('P', _('Jember')),
+ ('R', _('Banyumas')),
+ ('RI', _('Federal Government')),
+ ('S', _('Bojonegoro')),
+ ('T', _('Purwakarta')),
+ ('W', _('Sidoarjo')),
+ ('Z', _('Garut')),
+)
View
0 django/contrib/localflavor/ie/__init__.py
No changes.
View
13 django/contrib/localflavor/ie/forms.py
@@ -0,0 +1,13 @@
+"""
+UK-specific Form helpers
+"""
+
+from django.forms.fields import Select
+
+class IECountySelect(Select):
+ """
+ A Select widget that uses a list of Irish Counties as its choices.
+ """
+ def __init__(self, attrs=None):
+ from ie_counties import IE_COUNTY_CHOICES
+ super(IECountySelect, self).__init__(attrs, choices=IE_COUNTY_CHOICES)
View
40 django/contrib/localflavor/ie/ie_counties.py
@@ -0,0 +1,40 @@
+"""
+Sources:
+ Irish Counties: http://en.wikipedia.org/wiki/Counties_of_Ireland
+"""
+from django.utils.translation import ugettext_lazy as _
+
+IE_COUNTY_CHOICES = (
+ ('antrim', _('Antrim')),
+ ('armagh', _('Armagh')),
+ ('carlow', _('Carlow')),
+ ('cavan', _('Cavan')),
+ ('clare', _('Clare')),
+ ('cork', _('Cork')),
+ ('derry', _('Derry')),
+ ('donegal', _('Donegal')),
+ ('down', _('Down')),
+ ('dublin', _('Dublin')),
+ ('fermanagh', _('Fermanagh')),
+ ('galway', _('Galway')),
+ ('kerry', _('Kerry')),
+ ('kildare', _('Kildare')),
+ ('kilkenny', _('Kilkenny')),
+ ('laois', _('Laois')),
+ ('leitrim', _('Leitrim')),
+ ('limerick', _('Limerick')),
+ ('longford', _('Longford')),
+ ('louth', _('Louth')),
+ ('mayo', _('Mayo')),
+ ('meath', _('Meath')),
+ ('monaghan', _('Monaghan')),
+ ('offaly', _('Offaly')),
+ ('roscommon', _('Roscommon')),
+ ('sligo', _('Sligo')),
+ ('tipperary', _('Tipperary')),
+ ('tyrone', _('Tyrone')),
+ ('waterford', _('Waterford')),
+ ('westmeath', _('Westmeath')),
+ ('wexford', _('Wexford')),
+ ('wicklow', _('Wicklow')),
+)
View
0 django/contrib/localflavor/kw/__init__.py
No changes.
View
61 django/contrib/localflavor/kw/forms.py
@@ -0,0 +1,61 @@
+"""
+Kuwait-specific Form helpers
+"""
+import re
+from datetime import date
+from django.forms import ValidationError
+from django.forms.fields import Field, RegexField, EMPTY_VALUES
+from django.utils.translation import gettext as _
+
+id_re = re.compile(r'^(?P<initial>\d{1})(?P<yy>\d\d)(?P<mm>\d\d)(?P<dd>\d\d)(?P<mid>\d{4})(?P<checksum>\d{1})')
+
+class KWCivilIDNumberField(Field):
+ """
+ Kuwaiti Civil ID numbers are 12 digits, second to seventh digits
+ represents the person's birthdate.
+
+ Checks the following rules to determine the validty of the number:
+ * The number consist of 12 digits.
+ * The birthdate of the person is a valid date.
+ * The calculated checksum equals to the last digit of the Civil ID.
+ """
+ default_error_messages = {
+ 'invalid': _('Enter a valid Kuwaiti Civil ID number'),
+ }
+
+ def has_valid_checksum(self, value):
+ weight = (2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
+ calculated_checksum = 0
+ for i in range(11):
+ calculated_checksum += int(value[i]) * weight[i]
+
+ remainder = calculated_checksum % 11
+ checkdigit = 11 - remainder
+ if checkdigit != int(value[11]):
+ return False
+ return True
+
+ def clean(self, value):
+ super(KWCivilIDNumberField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ if not re.match(r'^\d{12}$', value):
+ raise ValidationError(self.error_messages['invalid'])
+
+ match = re.match(id_re, value)
+
+ if not match:
+ raise ValidationError(self.error_messages['invalid'])
+
+ gd = match.groupdict()
+
+ try:
+ d = date(int(gd['yy']), int(gd['mm']), int(gd['dd']))
+ except ValueError:
+ raise ValidationError(self.error_messages['invalid'])
+
+ if not self.has_valid_checksum(value):
+ raise ValidationError(self.error_messages['invalid'])
+
+ return value
View
0 django/contrib/localflavor/pt/__init__.py
No changes.
View
47 django/contrib/localflavor/pt/forms.py
@@ -0,0 +1,47 @@
+"""
+PT-specific Form helpers
+"""
+
+from django.forms import ValidationError
+from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
+from django.utils.encoding import smart_unicode
+from django.utils.translation import ugettext_lazy as _
+import re
+
+phone_digits_re = re.compile(r'^(\d{9}|(00|\+)\d*)$')
+
+
+class PTZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': _('Enter a zip code in the format XXXX-XXX.'),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(PTZipCodeField, self).__init__(r'^(\d{4}-\d{3}|\d{7})$',
+ max_length=None, min_length=None, *args, **kwargs)
+
+ def clean(self,value):
+ cleaned = super(PTZipCodeField, self).clean(value)
+ if len(cleaned) == 7:
+ return u'%s-%s' % (cleaned[:4],cleaned[4:])
+ else:
+ return cleaned
+
+class PTPhoneNumberField(Field):
+ """
+ Validate local Portuguese phone number (including international ones)
+ It should have 9 digits (may include spaces) or start by 00 or + (international)
+ """
+ default_error_messages = {
+ 'invalid': _('Phone numbers must have 9 digits, or start by + or 00.'),
+ }
+
+ def clean(self, value):
+ super(PTPhoneNumberField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+ value = re.sub('(\.|\s)', '', smart_unicode(value))
+ m = phone_digits_re.search(value)
+ if m:
+ return u'%s' % value
+ raise ValidationError(self.error_messages['invalid'])
View
2 django/contrib/localflavor/uk/forms.py
@@ -33,7 +33,7 @@ def clean(self, value):
# Put a single space before the incode (second part).
postcode = self.space_regex.sub(r' \1', postcode)
if not self.postcode_regex.search(postcode):
- raise ValidationError(self.default_error_messages['invalid'])
+ raise ValidationError(self.error_messages['invalid'])
return postcode
class UKCountySelect(Select):
View
4 django/contrib/localflavor/us/forms.py
@@ -4,7 +4,7 @@
from django.core.validators import EMPTY_VALUES
from django.forms import ValidationError
-from django.forms.fields import Field, RegexField, Select
+from django.forms.fields import Field, RegexField, Select, CharField
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _
import re
@@ -21,7 +21,7 @@ def __init__(self, *args, **kwargs):
super(USZipCodeField, self).__init__(r'^\d{5}(?:-\d{4})?$',
max_length=None, min_length=None, *args, **kwargs)
-class USPhoneNumberField(Field):
+class USPhoneNumberField(CharField):
default_error_messages = {
'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
}
View
16 django/contrib/localflavor/us/models.py
@@ -1,6 +1,6 @@
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
-from django.db.models.fields import Field, CharField
+from django.db.models.fields import CharField
from django.contrib.localflavor.us.us_states import STATE_CHOICES
class USStateField(CharField):
@@ -12,22 +12,16 @@ def __init__(self, *args, **kwargs):
kwargs['max_length'] = 2
super(USStateField, self).__init__(*args, **kwargs)
-class PhoneNumberField(Field):
+class PhoneNumberField(CharField):
description = _("Phone number")
- def get_internal_type(self):
- return "PhoneNumberField"
-
- def db_type(self, connection):
- if connection.settings_dict['ENGINE'] == 'django.db.backends.oracle':
- return 'VARCHAR2(20)'
- else:
- return 'varchar(20)'
+ def __init__(self, *args, **kwargs):
+ kwargs['max_length'] = 20
+ super(PhoneNumberField, self).__init__(*args, **kwargs)
def formfield(self, **kwargs):
from django.contrib.localflavor.us.forms import USPhoneNumberField
defaults = {'form_class': USPhoneNumberField}
defaults.update(kwargs)
return super(PhoneNumberField, self).formfield(**defaults)
-
View
0 django/contrib/localflavor/uy/__init__.py
No changes.
View
59 django/contrib/localflavor/uy/forms.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+"""
+UY-specific form helpers.
+"""
+import re
+
+from django.forms.fields import Select, RegexField, EMPTY_VALUES
+from django.forms import ValidationError
+from django.utils.translation import ugettext_lazy as _
+from django.contrib.localflavor.uy.util import get_validation_digit
+
+
+class UYDepartamentSelect(Select):
+ """
+ A Select widget that uses a list of Uruguayan departaments as its choices.
+ """
+ def __init__(self, attrs=None):
+ from uy_departaments import DEPARTAMENT_CHOICES
+ super(UYDepartamentSelect, self).__init__(attrs, choices=DEPARTAMENT_CHOICES)
+
+
+class UYCIField(RegexField):
+ """
+ A field that validates Uruguayan 'Cedula de identidad' (CI) numbers.
+ """
+ default_error_messages = {
+ 'invalid': _("Enter a valid CI number in X.XXX.XXX-X,"
+ "XXXXXXX-X or XXXXXXXX format."),
+ 'invalid_validation_digit': _("Enter a valid CI number."),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(UYCIField, self).__init__(r'(?P<num>(\d{6,7}|(\d\.)?\d{3}\.\d{3}))-?(?P<val>\d)',
+ *args, **kwargs)
+
+ def clean(self, value):
+ """
+ Validates format and validation digit.
+
+ The official format is [X.]XXX.XXX-X but usually dots and/or slash are
+ omitted so, when validating, those characters are ignored if found in
+ the correct place. The three typically used formats are supported:
+ [X]XXXXXXX, [X]XXXXXX-X and [X.]XXX.XXX-X.
+ """
+
+ value = super(UYCIField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+ match = self.regex.match(value)
+ if not match:
+ raise ValidationError(self.error_messages['invalid'])
+
+ number = int(match.group('num').replace('.', ''))
+ validation_digit = int(match.group('val'))
+
+ if not validation_digit == get_validation_digit(number):
+ raise ValidationError(self.error_messages['invalid_validation_digit'])
+
+ return value
View
12 django/contrib/localflavor/uy/util.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+
+def get_validation_digit(number):
+ """ Calculates the validation digit for the given number. """
+ sum = 0
+ dvs = [4, 3, 6, 7, 8, 9, 2]
+ number = str(number)
+
+ for i in range(0, len(number)):
+ sum = (int(number[-1 - i]) * dvs[i] + sum) % 10
+
+ return (10-sum) % 10
View
24 django/contrib/localflavor/uy/uy_departaments.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+"""A list of Urguayan departaments as `choices` in a formfield."""
+
+DEPARTAMENT_CHOICES = (
+ ('G', u'Artigas'),
+ ('A', u'Canelones'),
+ ('E', u'Cerro Largo'),
+ ('L', u'Colonia'),
+ ('Q', u'Durazno'),
+ ('N', u'Flores'),
+ ('O', u'Florida'),
+ ('P', u'Lavalleja'),
+ ('B', u'Maldonado'),
+ ('S', u'Montevideo'),
+ ('I', u'Paysandú'),
+ ('J', u'Río Negro'),
+ ('F', u'Rivera'),
+ ('C', u'Rocha'),
+ ('H', u'Salto'),
+ ('M', u'San José'),
+ ('K', u'Soriano'),
+ ('R', u'Tacuarembó'),
+ ('D', u'Treinta y Tres'),
+)
View
6 django/core/management/commands/test.py
@@ -17,18 +17,18 @@ class Command(BaseCommand):
def handle(self, *test_labels, **options):
from django.conf import settings
from django.test.utils import get_runner
-
+
verbosity = int(options.get('verbosity', 1))
interactive = options.get('interactive', True)
failfast = options.get('failfast', False)
test_runner = get_runner(settings)
# Some custom test runners won't accept the failfast flag, so let's make sure they accept it before passing it to them
if 'failfast' in test_runner.func_code.co_varnames:
- failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive,
+ failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive,
failfast=failfast)
else:
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
if failures:
- sys.exit(failures)
+ sys.exit(bool(failures))
View
19 django/forms/widgets.py
@@ -10,8 +10,7 @@
from django.utils.translation import ugettext
from django.utils.encoding import StrAndUnicode, force_unicode
from django.utils.safestring import mark_safe
-from django.utils.formats import localize
-from django.utils import datetime_safe
+from django.utils import datetime_safe, formats
from datetime import time
from util import flatatt
from urlparse import urljoin
@@ -209,7 +208,7 @@ def render(self, name, value, attrs=None):
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
if value != '':
# Only add the 'value' attribute if a value is non-empty.
- final_attrs['value'] = force_unicode(localize(value, is_input=True))
+ final_attrs['value'] = force_unicode(formats.localize_input(value))
return mark_safe(u'<input%s />' % flatatt(final_attrs))
class TextInput(Input):
@@ -284,7 +283,7 @@ def render(self, name, value, attrs=None):
class DateInput(Input):
input_type = 'text'
- format = '%Y-%m-%d' # '2006-10-25'
+ format = None
def __init__(self, attrs=None, format=None):
super(DateInput, self).__init__(attrs)
@@ -295,8 +294,7 @@ def _format_value(self, value):
if value is None:
return ''
elif hasattr(value, 'strftime'):
- value = datetime_safe.new_date(value)
- return value.strftime(self.format)
+ return formats.localize_input(value, self.format)
return value
def render(self, name, value, attrs=None):
@@ -308,7 +306,7 @@ def _has_changed(self, initial, data):
class DateTimeInput(Input):
input_type = 'text'
- format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59'
+ format = None
def __init__(self, attrs=None, format=None):
super(DateTimeInput, self).__init__(attrs)
@@ -319,8 +317,7 @@ def _format_value(self, value):
if value is None:
return ''
elif hasattr(value, 'strftime'):
- value = datetime_safe.new_datetime(value)
- return value.strftime(self.format)
+ return formats.localize_input(value, self.format)
return value
def render(self, name, value, attrs=None):
@@ -332,7 +329,7 @@ def _has_changed(self, initial, data):
class TimeInput(Input):
input_type = 'text'
- format = '%H:%M:%S' # '14:30:59'
+ format = None
def __init__(self, attrs=None, format=None):
super(TimeInput, self).__init__(attrs)
@@ -343,7 +340,7 @@ def _format_value(self, value):
if value is None:
return ''
elif hasattr(value, 'strftime'):
- return value.strftime(self.format)
+ return formats.localize_input(value, self.format)
return value
def render(self, name, value, attrs=None):
View
14 django/template/defaultfilters.py
@@ -15,10 +15,10 @@
from django.template import Variable, Library
from django.conf import settings
+from django.utils import formats
from django.utils.translation import ugettext, ungettext
from django.utils.encoding import force_unicode, iri_to_uri
from django.utils.safestring import mark_safe, SafeData
-from django.utils.formats import date_format, number_format
register = Library()
@@ -167,14 +167,14 @@ def floatformat(text, arg=-1):
return input_val
if not m and p < 0:
- return mark_safe(number_format(u'%d' % (int(d)), 0))
+ return mark_safe(formats.number_format(u'%d' % (int(d)), 0))
if p == 0:
exp = Decimal(1)
else:
exp = Decimal('1.0') / (Decimal(10) ** abs(p))
try:
- return mark_safe(number_format(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)), abs(p)))