summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHonza Král <honza.kral@gmail.com>2010-01-04 01:06:26 +0000
committerHonza Král <honza.kral@gmail.com>2010-01-04 01:06:26 +0000
commitffd5564a87c5c8e5d5e1e201ae3462807b955efc (patch)
tree80c63887e1e9a9a2d379e655ad038f25f3982697
parenta7c320fea79b8750ab2a8aca84d8e8fb76d8b100 (diff)
[soc2009/model-validation] Merged to trunk at r12070
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@12073 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--AUTHORS6
-rw-r--r--django/conf/global_settings.py1
-rw-r--r--django/conf/locale/it/formats.py47
-rw-r--r--django/conf/locale/no/formats.py16
-rw-r--r--django/conf/locale/pl/LC_MESSAGES/django.po49
-rw-r--r--django/contrib/admin/media/js/admin/DateTimeShortcuts.js44
-rw-r--r--django/contrib/admin/media/js/core.js39
-rw-r--r--django/contrib/admin/options.py32
-rw-r--r--django/contrib/admin/templatetags/admin_list.py4
-rw-r--r--django/contrib/admin/util.py3
-rw-r--r--django/contrib/admin/widgets.py12
-rw-r--r--django/contrib/auth/models.py27
-rw-r--r--django/contrib/auth/tests/auth_backends.py15
-rw-r--r--django/contrib/databrowse/datastructures.py2
-rw-r--r--django/contrib/databrowse/plugins/calendars.py2
-rw-r--r--django/contrib/databrowse/templates/databrowse/calendar_day.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/calendar_main.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/calendar_month.html4
-rw-r--r--django/contrib/gis/db/backends/postgis/operations.py14
-rw-r--r--django/contrib/gis/db/backends/spatialite/base.py29
-rw-r--r--django/contrib/gis/db/backends/spatialite/creation.py3
-rw-r--r--django/contrib/gis/db/backends/spatialite/operations.py21
-rw-r--r--django/contrib/gis/db/models/manager.py5
-rw-r--r--django/contrib/gis/db/models/sql/compiler.py4
-rw-r--r--django/contrib/gis/geos/prototypes/errcheck.py9
-rw-r--r--django/contrib/gis/tests/relatedapp/tests.py2
-rw-r--r--django/contrib/localflavor/id/__init__.py0
-rw-r--r--django/contrib/localflavor/id/forms.py210
-rw-r--r--django/contrib/localflavor/id/id_choices.py101
-rw-r--r--django/contrib/localflavor/ie/__init__.py0
-rw-r--r--django/contrib/localflavor/ie/forms.py13
-rw-r--r--django/contrib/localflavor/ie/ie_counties.py40
-rw-r--r--django/contrib/localflavor/kw/__init__.py0
-rw-r--r--django/contrib/localflavor/kw/forms.py61
-rw-r--r--django/contrib/localflavor/pt/__init__.py0
-rw-r--r--django/contrib/localflavor/pt/forms.py47
-rw-r--r--django/contrib/localflavor/uk/forms.py2
-rw-r--r--django/contrib/localflavor/us/forms.py4
-rw-r--r--django/contrib/localflavor/us/models.py16
-rw-r--r--django/contrib/localflavor/uy/__init__.py0
-rw-r--r--django/contrib/localflavor/uy/forms.py59
-rw-r--r--django/contrib/localflavor/uy/util.py12
-rw-r--r--django/contrib/localflavor/uy/uy_departaments.py24
-rw-r--r--django/core/management/commands/test.py6
-rw-r--r--django/forms/widgets.py19
-rw-r--r--django/template/defaultfilters.py14
-rw-r--r--django/test/simple.py41
-rw-r--r--django/utils/datastructures.py7
-rw-r--r--django/utils/dateformat.py14
-rw-r--r--django/utils/encoding.py17
-rw-r--r--django/utils/formats.py56
-rw-r--r--django/utils/translation/trans_real.py3
-rw-r--r--django/views/i18n.py25
-rw-r--r--docs/ref/contrib/localflavor.txt103
-rw-r--r--docs/ref/django-admin.txt6
-rw-r--r--docs/ref/settings.txt2
-rw-r--r--docs/ref/templates/builtins.txt2
-rw-r--r--docs/releases/1.1.2.txt15
-rw-r--r--docs/releases/1.2.txt20
-rw-r--r--docs/topics/auth.txt2
-rw-r--r--docs/topics/testing.txt21
-rw-r--r--tests/regressiontests/admin_views/tests.py11
-rw-r--r--tests/regressiontests/admin_widgets/models.py9
-rw-r--r--tests/regressiontests/admin_widgets/tests.py3
-rw-r--r--tests/regressiontests/datastructures/tests.py7
-rw-r--r--tests/regressiontests/dateformat/tests.py4
-rw-r--r--tests/regressiontests/forms/localflavor/id.py175
-rw-r--r--tests/regressiontests/forms/localflavor/ie.py12
-rw-r--r--tests/regressiontests/forms/localflavor/kw.py15
-rw-r--r--tests/regressiontests/forms/localflavor/pt.py106
-rw-r--r--tests/regressiontests/forms/localflavor/uk.py11
-rw-r--r--tests/regressiontests/forms/localflavor/uy.py46
-rw-r--r--tests/regressiontests/forms/tests.py10
-rw-r--r--tests/regressiontests/forms/widgets.py36
-rw-r--r--tests/regressiontests/i18n/forms.py4
-rw-r--r--tests/regressiontests/i18n/models.py6
-rw-r--r--tests/regressiontests/i18n/tests.py67
-rw-r--r--tests/regressiontests/localflavor/forms.py7
-rw-r--r--tests/regressiontests/localflavor/models.py8
-rw-r--r--tests/regressiontests/localflavor/tests.py83
-rw-r--r--tests/regressiontests/settings/__init__.py4
-rw-r--r--tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.mobin0 -> 431 bytes
-rw-r--r--tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.po20
-rw-r--r--tests/regressiontests/views/tests/i18n.py6
-rwxr-xr-xtests/runtests.py2
85 files changed, 1652 insertions, 356 deletions
diff --git a/AUTHORS b/AUTHORS
index e60cf61c41..6ad345a971 100644
--- a/AUTHORS
+++ b/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,6 +190,7 @@ 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>
@@ -195,6 +198,7 @@ answer newbie questions, and generally made Django that much better:
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
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index e970c12792..e3ba6602cc 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -84,6 +84,7 @@ LANGUAGES = (
('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')),
diff --git a/django/conf/locale/it/formats.py b/django/conf/locale/it/formats.py
index fe86b5bd70..8471bc0392 100644
--- a/django/conf/locale/it/formats.py
+++ b/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
diff --git a/django/conf/locale/no/formats.py b/django/conf/locale/no/formats.py
index 62ee886f11..0ebf629d9c 100644
--- a/django/conf/locale/no/formats.py
+++ b/django/conf/locale/no/formats.py
@@ -14,19 +14,19 @@ DATE_INPUT_FORMATS = (
'%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 = ','
diff --git a/django/conf/locale/pl/LC_MESSAGES/django.po b/django/conf/locale/pl/LC_MESSAGES/django.po
index 0438e80433..8fb0f9fec8 100644
--- a/django/conf/locale/pl/LC_MESSAGES/django.po
+++ b/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,94 +314,94 @@ 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."
@@ -409,12 +409,12 @@ 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"
diff --git a/django/contrib/admin/media/js/admin/DateTimeShortcuts.js b/django/contrib/admin/media/js/admin/DateTimeShortcuts.js
index e3ebd03fb2..0c6eff0fa9 100644
--- a/django/contrib/admin/media/js/admin/DateTimeShortcuts.js
+++ b/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];
+ 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);
- }
- }
+ // 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) {
diff --git a/django/contrib/admin/media/js/core.js b/django/contrib/admin/media/js/core.js
index c8d0db6a8d..a7192c17f1 100644
--- a/django/contrib/admin/media/js/core.js
+++ b/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
// ----------------------------------------------------------------------------
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 3f79a81d56..5085d884a9 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -8,7 +8,7 @@ from django.contrib.admin import helpers
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 @@ class ModelAdmin(BaseModelAdmin):
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 @@ class ModelAdmin(BaseModelAdmin):
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 @@ class ModelAdmin(BaseModelAdmin):
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
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index 352277351e..8626ef0c23 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/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)
diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
index 8b2435ef33..4a9f49cb7c 100644
--- a/django/contrib/admin/util.py
+++ b/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)
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index 0392b642bb..120df94cf3 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -41,21 +41,21 @@ class FilteredSelectMultiple(forms.SelectMultiple):
(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):
"""
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
index 8148d8a992..ceab7baf12 100644
--- a/django/contrib/auth/models.py
+++ b/django/contrib/auth/models.py
@@ -218,22 +218,26 @@ class User(models.Model):
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 @@ class User(models.Model):
# 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
diff --git a/django/contrib/auth/tests/auth_backends.py b/django/contrib/auth/tests/auth_backends.py
index bf5611aef0..af15d0b03b 100644
--- a/django/contrib/auth/tests/auth_backends.py
+++ b/django/contrib/auth/tests/auth_backends.py
@@ -69,6 +69,21 @@ class BackendTest(TestCase):
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
diff --git a/django/contrib/databrowse/datastructures.py b/django/contrib/databrowse/datastructures.py
index 369f825f0e..4fcdba9d33 100644
--- a/django/contrib/databrowse/datastructures.py
+++ b/django/contrib/databrowse/datastructures.py
@@ -158,7 +158,7 @@ class EasyInstanceField(object):
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:
diff --git a/django/contrib/databrowse/plugins/calendars.py b/django/contrib/databrowse/plugins/calendars.py
index ac2f522148..9bbd02da26 100644
--- a/django/contrib/databrowse/plugins/calendars.py
+++ b/django/contrib/databrowse/plugins/calendars.py
@@ -37,7 +37,7 @@ class CalendarPlugin(DatabrowsePlugin):
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))]
diff --git a/django/contrib/databrowse/templates/databrowse/calendar_day.html b/django/contrib/databrowse/templates/databrowse/calendar_day.html
index b0a221d5d3..c009a94c66 100644
--- a/django/contrib/databrowse/templates/databrowse/calendar_day.html
+++ b/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>
diff --git a/django/contrib/databrowse/templates/databrowse/calendar_main.html b/django/contrib/databrowse/templates/databrowse/calendar_main.html
index b22a44d321..7cb59042df 100644
--- a/django/contrib/databrowse/templates/databrowse/calendar_main.html
+++ b/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>
diff --git a/django/contrib/databrowse/templates/databrowse/calendar_month.html b/django/contrib/databrowse/templates/databrowse/calendar_month.html
index f6a616911b..ad189f441c 100644
--- a/django/contrib/databrowse/templates/databrowse/calendar_month.html
+++ b/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 %}
diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py
index 98dab75270..030599dfff 100644
--- a/django/contrib/gis/db/backends/postgis/operations.py
+++ b/django/contrib/gis/db/backends/postgis/operations.py
@@ -8,8 +8,7 @@ from django.contrib.gis.db.backends.postgis.adapter import PostGISAdapter
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 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
"""
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]
diff --git a/django/contrib/gis/db/backends/spatialite/base.py b/django/contrib/gis/db/backends/spatialite/base.py
index 0b6332dd0c..9dde4ec0b2 100644
--- a/django/contrib/gis/db/backends/spatialite/base.py
+++ b/django/contrib/gis/db/backends/spatialite/base.py
@@ -9,7 +9,6 @@ from django.contrib.gis.db.backends.spatialite.client import SpatiaLiteClient
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 @@ class DatabaseWrapper(SqliteDatabaseWrapper):
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 @@ class DatabaseWrapper(SqliteDatabaseWrapper):
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 @@ class DatabaseWrapper(SqliteDatabaseWrapper):
'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)
diff --git a/django/contrib/gis/db/backends/spatialite/creation.py b/django/contrib/gis/db/backends/spatialite/creation.py
index 1bec8360df..cbe4a29585 100644
--- a/django/contrib/gis/db/backends/spatialite/creation.py
+++ b/django/contrib/gis/db/backends/spatialite/creation.py
@@ -24,8 +24,7 @@ class SpatiaLiteCreation(DatabaseCreation):
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)
diff --git a/django/contrib/gis/db/backends/spatialite/operations.py b/django/contrib/gis/db/backends/spatialite/operations.py
index 7b4e4364ad..444a212c13 100644
--- a/django/contrib/gis/db/backends/spatialite/operations.py
+++ b/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 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
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 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
"""
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 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
"""
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
diff --git a/django/contrib/gis/db/models/manager.py b/django/contrib/gis/db/models/manager.py
index b39d652d0d..041bc0dcd5 100644
--- a/django/contrib/gis/db/models/manager.py
+++ b/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)
diff --git a/django/contrib/gis/db/models/sql/compiler.py b/django/contrib/gis/db/models/sql/compiler.py
index 1a91f8eeba..a94093fec3 100644
--- a/django/contrib/gis/db/models/sql/compiler.py
+++ b/django/contrib/gis/db/models/sql/compiler.py
@@ -191,8 +191,8 @@ class GeoSQLCompiler(compiler.SQLCompiler):
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)
diff --git a/django/contrib/gis/geos/prototypes/errcheck.py b/django/contrib/gis/geos/prototypes/errcheck.py
index 0d5e9e24fc..a00b93be6e 100644
--- a/django/contrib/gis/geos/prototypes/errcheck.py
+++ b/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 @@ if GEOS_VERSION >= (3, 1, 1):
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 ###
diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py
index 8ebe871e6d..184b65b9c7 100644
--- a/django/contrib/gis/tests/relatedapp/tests.py
+++ b/django/contrib/gis/tests/relatedapp/tests.py
@@ -19,7 +19,6 @@ class RelatedGeoModelTest(unittest.TestCase):
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 @@ class RelatedGeoModelTest(unittest.TestCase):
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
diff --git a/django/contrib/localflavor/id/__init__.py b/django/contrib/localflavor/id/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/localflavor/id/__init__.py
diff --git a/django/contrib/localflavor/id/forms.py b/django/contrib/localflavor/id/forms.py
new file mode 100644
index 0000000000..0d68fa32d5
--- /dev/null
+++ b/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:])
diff --git a/django/contrib/localflavor/id/id_choices.py b/django/contrib/localflavor/id/id_choices.py
new file mode 100644
index 0000000000..ed1ea017b9
--- /dev/null
+++ b/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')),
+)
diff --git a/django/contrib/localflavor/ie/__init__.py b/django/contrib/localflavor/ie/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/localflavor/ie/__init__.py
diff --git a/django/contrib/localflavor/ie/forms.py b/django/contrib/localflavor/ie/forms.py
new file mode 100644
index 0000000000..2cfd2f2bcc
--- /dev/null
+++ b/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)
diff --git a/django/contrib/localflavor/ie/ie_counties.py b/django/contrib/localflavor/ie/ie_counties.py
new file mode 100644
index 0000000000..c8991e01ae
--- /dev/null
+++ b/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')),
+)
diff --git a/django/contrib/localflavor/kw/__init__.py b/django/contrib/localflavor/kw/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/localflavor/kw/__init__.py
diff --git a/django/contrib/localflavor/kw/forms.py b/django/contrib/localflavor/kw/forms.py
new file mode 100644
index 0000000000..32d6cb990a
--- /dev/null
+++ b/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
diff --git a/django/contrib/localflavor/pt/__init__.py b/django/contrib/localflavor/pt/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/localflavor/pt/__init__.py
diff --git a/django/contrib/localflavor/pt/forms.py b/django/contrib/localflavor/pt/forms.py
new file mode 100644
index 0000000000..86833fc85f
--- /dev/null
+++ b/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'])
diff --git a/django/contrib/localflavor/uk/forms.py b/django/contrib/localflavor/uk/forms.py
index 7df736444a..aafe9734db 100644
--- a/django/contrib/localflavor/uk/forms.py
+++ b/django/contrib/localflavor/uk/forms.py
@@ -33,7 +33,7 @@ class UKPostcodeField(CharField):
# 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):
diff --git a/django/contrib/localflavor/us/forms.py b/django/contrib/localflavor/us/forms.py
index 7879e323f6..dc6daaf165 100644
--- a/django/contrib/localflavor/us/forms.py
+++ b/django/contrib/localflavor/us/forms.py
@@ -4,7 +4,7 @@ USA-specific Form helpers
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 @@ class USZipCodeField(RegexField):
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.',
}
diff --git a/django/contrib/localflavor/us/models.py b/django/contrib/localflavor/us/models.py
index 4cb6e6851e..1f78e4504a 100644
--- a/django/contrib/localflavor/us/models.py
+++ b/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 @@ class USStateField(CharField):
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)
-
diff --git a/django/contrib/localflavor/uy/__init__.py b/django/contrib/localflavor/uy/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/localflavor/uy/__init__.py
diff --git a/django/contrib/localflavor/uy/forms.py b/django/contrib/localflavor/uy/forms.py
new file mode 100644
index 0000000000..5b47ba1e24
--- /dev/null
+++ b/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
diff --git a/django/contrib/localflavor/uy/util.py b/django/contrib/localflavor/uy/util.py
new file mode 100644
index 0000000000..0c1a8f84be
--- /dev/null
+++ b/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
diff --git a/django/contrib/localflavor/uy/uy_departaments.py b/django/contrib/localflavor/uy/uy_departaments.py
new file mode 100644
index 0000000000..97795f8e82
--- /dev/null
+++ b/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'),
+)
diff --git a/django/core/management/commands/test.py b/django/core/management/commands/test.py
index 4fd6ba0c8d..46204a7508 100644
--- a/django/core/management/commands/test.py
+++ b/django/core/management/commands/test.py
@@ -17,7 +17,7 @@ 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)
@@ -25,10 +25,10 @@ class Command(BaseCommand):
# 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))
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index aabd1b017d..c16c239de1 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -10,8 +10,7 @@ from django.utils.html import escape, conditional_escape
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 @@ class Input(Widget):
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 @@ class Textarea(Widget):
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 @@ class DateInput(Input):
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 @@ class DateInput(Input):
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 @@ class DateTimeInput(Input):
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 @@ class DateTimeInput(Input):
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 @@ class TimeInput(Input):
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):
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 26b6b5ec4e..0ba16bc9a5 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -15,10 +15,10 @@ except ImportError:
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)))
+ return mark_safe(formats.number_format(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)), abs(p)))
except InvalidOperation:
return input_val
floatformat.is_safe = True
@@ -686,7 +686,7 @@ def date(value, arg=None):
if arg is None:
arg = settings.DATE_FORMAT
try:
- return date_format(value, arg)
+ return formats.date_format(value, arg)
except AttributeError:
try:
return format(value, arg)
@@ -696,16 +696,16 @@ date.is_safe = False
def time(value, arg=None):
"""Formats a time according to the given format."""
- from django.utils.dateformat import time_format
+ from django.utils import dateformat
if value in (None, u''):
return u''
if arg is None:
arg = settings.TIME_FORMAT
try:
- return date_format(value, arg)
+ return formats.time_format(value, arg)
except AttributeError:
try:
- return time_format(value, arg)
+ return dateformat.time_format(value, arg)
except AttributeError:
return ''
time.is_safe = False
diff --git a/django/test/simple.py b/django/test/simple.py
index 092ef4bde2..b01ecad7f4 100644
--- a/django/test/simple.py
+++ b/django/test/simple.py
@@ -1,4 +1,7 @@
+import sys
+import signal
import unittest
+
from django.conf import settings
from django.db.models import get_app, get_apps
from django.test import _doctest as doctest
@@ -11,22 +14,50 @@ TEST_MODULE = 'tests'
doctestOutputChecker = OutputChecker()
class DjangoTestRunner(unittest.TextTestRunner):
-
+
def __init__(self, verbosity=0, failfast=False, **kwargs):
super(DjangoTestRunner, self).__init__(verbosity=verbosity, **kwargs)
self.failfast = failfast
-
+ self._keyboard_interrupt_intercepted = False
+
+ def run(self, *args, **kwargs):
+ """
+ Runs the test suite after registering a custom signal handler
+ that triggers a graceful exit when Ctrl-C is pressed.
+ """
+ self._default_keyboard_interrupt_handler = signal.signal(signal.SIGINT,
+ self._keyboard_interrupt_handler)
+ try:
+ result = super(DjangoTestRunner, self).run(*args, **kwargs)
+ finally:
+ signal.signal(signal.SIGINT, self._default_keyboard_interrupt_handler)
+ return result
+
+ def _keyboard_interrupt_handler(self, signal_number, stack_frame):
+ """
+ Handles Ctrl-C by setting a flag that will stop the test run when
+ the currently running test completes.
+ """
+ self._keyboard_interrupt_intercepted = True
+ sys.stderr.write(" <Test run halted by Ctrl-C> ")
+ # Set the interrupt handler back to the default handler, so that
+ # another Ctrl-C press will trigger immediate exit.
+ signal.signal(signal.SIGINT, self._default_keyboard_interrupt_handler)
+
def _makeResult(self):
result = super(DjangoTestRunner, self)._makeResult()
failfast = self.failfast
-
+
def stoptest_override(func):
def stoptest(test):
- if failfast and not result.wasSuccessful():
+ # If we were set to failfast and the unit test failed,
+ # or if the user has typed Ctrl-C, report and quit
+ if (failfast and not result.wasSuccessful()) or \
+ self._keyboard_interrupt_intercepted:
result.stop()
func(test)
return stoptest
-
+
setattr(result, 'stopTest', stoptest_override(result.stopTest))
return result
diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py
index 06cf6c6b75..adcc3546d0 100644
--- a/django/utils/datastructures.py
+++ b/django/utils/datastructures.py
@@ -1,3 +1,5 @@
+from types import GeneratorType
+
from django.utils.copycompat import deepcopy
@@ -65,6 +67,11 @@ class SortedDict(dict):
def __init__(self, data=None):
if data is None:
data = {}
+ elif isinstance(data, GeneratorType):
+ # Unfortunately we need to be able to read a generator twice. Once
+ # to get the data into self with our super().__init__ call and a
+ # second time to setup keyOrder correctly
+ data = list(data)
super(SortedDict, self).__init__(data)
if isinstance(data, dict):
self.keyOrder = data.keys()
diff --git a/django/utils/dateformat.py b/django/utils/dateformat.py
index b2ea71228c..13cc2c7a81 100644
--- a/django/utils/dateformat.py
+++ b/django/utils/dateformat.py
@@ -19,7 +19,7 @@ from django.utils.tzinfo import LocalTimezone
from django.utils.translation import ugettext as _
from django.utils.encoding import force_unicode
-re_formatchars = re.compile(r'(?<!\\)([aAbBdDfFgGhHiIjlLmMnNOPrsStTUwWyYzZ])')
+re_formatchars = re.compile(r'(?<!\\)([aAbBcdDfFgGhHiIjlLmMnNOPrsStTUuwWyYzZ])')
re_escaped = re.compile(r'\\(.)')
class Formatter(object):
@@ -104,6 +104,11 @@ class TimeFormat(Formatter):
"Seconds; i.e. '00' to '59'"
return u'%02d' % self.data.second
+ def u(self):
+ "Microseconds"
+ return self.data.microsecond
+
+
class DateFormat(TimeFormat):
year_days = [None, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
@@ -118,6 +123,13 @@ class DateFormat(TimeFormat):
"Month, textual, 3 letters, lowercase; e.g. 'jan'"
return MONTHS_3[self.data.month]
+ def c(self):
+ """
+ ISO 8601 Format
+ Example : '2008-01-02T10:30:00.000123'
+ """
+ return self.data.isoformat(' ')
+
def d(self):
"Day of the month, 2 digits with leading zeros; i.e. '01' to '31'"
return u'%02d' % self.data.day
diff --git a/django/utils/encoding.py b/django/utils/encoding.py
index 335f1a1551..66e6ebdd76 100644
--- a/django/utils/encoding.py
+++ b/django/utils/encoding.py
@@ -131,12 +131,21 @@ def iri_to_uri(iri):
Returns an ASCII string containing the encoded result.
"""
- # The list of safe characters here is constructed from the printable ASCII
- # characters that are not explicitly excluded by the list at the end of
- # section 3.1 of RFC 3987.
+ # The list of safe characters here is constructed from the "reserved" and
+ # "unreserved" characters specified in sections 2.2 and 2.3 of RFC 3986:
+ # reserved = gen-delims / sub-delims
+ # gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+ # sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ # / "*" / "+" / "," / ";" / "="
+ # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ # Of the unreserved characters, urllib.quote already considers all but
+ # the ~ safe.
+ # The % character is also added to the list of safe characters here, as the
+ # end of section 3.1 of RFC 3987 specifically mentions that % must not be
+ # converted.
if iri is None:
return iri
- return urllib.quote(smart_str(iri), safe='/#%[]=:;$&()+,!?*')
+ return urllib.quote(smart_str(iri), safe="/#%[]=:;$&()+,!?*@'~")
# The encoding of the default system locale but falls back to the
diff --git a/django/utils/formats.py b/django/utils/formats.py
index e18e120b36..77479ae2a6 100644
--- a/django/utils/formats.py
+++ b/django/utils/formats.py
@@ -4,15 +4,15 @@ import datetime
from django.conf import settings
from django.utils.translation import get_language, to_locale, check_for_language
from django.utils.importlib import import_module
-from django.utils import dateformat
-from django.utils import numberformat
+from django.utils.encoding import smart_str
+from django.utils import dateformat, numberformat, datetime_safe
-def get_format_modules():
+def get_format_modules(reverse=False):
"""
Returns an iterator over the format modules found in the project and Django
"""
modules = []
- if not check_for_language(get_language()):
+ if not check_for_language(get_language()) or not settings.USE_L10N:
return modules
locale = to_locale(get_language())
if settings.FORMAT_MODULE_PATH:
@@ -30,6 +30,8 @@ def get_format_modules():
# Don't return duplicates
if mod not in modules:
modules.append(mod)
+ if reverse:
+ modules.reverse()
return modules
def get_format(format_type):
@@ -38,6 +40,7 @@ def get_format(format_type):
language (locale), defaults to the format in the settings.
format_type is the name of the format, e.g. 'DATE_FORMAT'
"""
+ format_type = smart_str(format_type)
if settings.USE_L10N:
for module in get_format_modules():
try:
@@ -53,6 +56,12 @@ def date_format(value, format=None):
"""
return dateformat.format(value, get_format(format or 'DATE_FORMAT'))
+def time_format(value, format=None):
+ """
+ Formats a datetime.time object using a localizable format
+ """
+ return dateformat.time_format(value, get_format(format or 'TIME_FORMAT'))
+
def number_format(value, decimal_pos=None):
"""
Formats a numeric value using localization settings
@@ -65,11 +74,10 @@ def number_format(value, decimal_pos=None):
get_format('THOUSAND_SEPARATOR'),
)
-def localize(value, is_input=False):
+def localize(value):
"""
- Checks value, and if it has a localizable type (date,
- number...) it returns the value as a string using
- current locale format
+ Checks if value is a localizable type (date, number...) and returns it
+ formatted as a string using current locale format
"""
if settings.USE_L10N:
if isinstance(value, decimal.Decimal):
@@ -79,19 +87,27 @@ def localize(value, is_input=False):
elif isinstance(value, int):
return number_format(value)
elif isinstance(value, datetime.datetime):
- if not is_input:
- return date_format(value, 'DATETIME_FORMAT')
- else:
- return value.strftime(get_format('DATETIME_INPUT_FORMATS')[0])
+ return date_format(value, 'DATETIME_FORMAT')
elif isinstance(value, datetime.date):
- if not is_input:
- return date_format(value)
- else:
- return value.strftime(get_format('DATE_INPUT_FORMATS')[0])
+ return date_format(value)
elif isinstance(value, datetime.time):
- if not is_input:
- return date_format(value, 'TIME_FORMAT')
- else:
- return value.strftime(get_format('TIME_INPUT_FORMATS')[0])
+ return time_format(value, 'TIME_FORMAT')
return value
+def localize_input(value, default=None):
+ """
+ Checks if an input value is a localizable type and returns it
+ formatted with the appropriate formatting string of the current locale.
+ """
+ if isinstance(value, datetime.datetime):
+ value = datetime_safe.new_datetime(value)
+ format = smart_str(default or get_format('DATETIME_INPUT_FORMATS')[0])
+ return value.strftime(format)
+ elif isinstance(value, datetime.date):
+ value = datetime_safe.new_date(value)
+ format = smart_str(default or get_format('DATE_INPUT_FORMATS')[0])
+ return value.strftime(format)
+ elif isinstance(value, datetime.time):
+ format = smart_str(default or get_format('TIME_INPUT_FORMATS')[0])
+ return value.strftime(format)
+ return value
diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py
index 8b7db0f123..3f13d79290 100644
--- a/django/utils/translation/trans_real.py
+++ b/django/utils/translation/trans_real.py
@@ -41,6 +41,9 @@ def to_locale(language, to_lower=False):
if to_lower:
return language[:p].lower()+'_'+language[p+1:].lower()
else:
+ # Get correct locale for sr-latn
+ if len(language[p+1:]) > 2:
+ return language[:p].lower()+'_'+language[p+1].upper()+language[p+2:].lower()
return language[:p].lower()+'_'+language[p+1:].upper()
else:
return language.lower()
diff --git a/django/views/i18n.py b/django/views/i18n.py
index ddd75203a7..6a1ee514cd 100644
--- a/django/views/i18n.py
+++ b/django/views/i18n.py
@@ -6,6 +6,7 @@ from django.conf import settings
from django.utils import importlib
from django.utils.translation import check_for_language, activate, to_locale, get_language
from django.utils.text import javascript_quote
+from django.utils.encoding import smart_unicode
from django.utils.formats import get_format_modules
def set_language(request):
@@ -36,15 +37,17 @@ def set_language(request):
def get_formats():
"""
- Returns an iterator over all formats in formats file
+ Returns all formats strings required for i18n to work
"""
- FORMAT_SETTINGS = ('DATE_FORMAT', 'DATETIME_FORMAT', 'TIME_FORMAT',
+ FORMAT_SETTINGS = (
+ 'DATE_FORMAT', 'DATETIME_FORMAT', 'TIME_FORMAT',
'YEAR_MONTH_FORMAT', 'MONTH_DAY_FORMAT', 'SHORT_DATE_FORMAT',
'SHORT_DATETIME_FORMAT', 'FIRST_DAY_OF_WEEK', 'DECIMAL_SEPARATOR',
- 'THOUSAND_SEPARATOR', 'NUMBER_GROUPING')
-
+ 'THOUSAND_SEPARATOR', 'NUMBER_GROUPING',
+ 'DATE_INPUT_FORMATS', 'TIME_INPUT_FORMATS', 'DATETIME_INPUT_FORMATS'
+ )
result = {}
- for module in [settings] + get_format_modules():
+ for module in [settings] + get_format_modules(reverse=True):
for attr in FORMAT_SETTINGS:
try:
result[attr] = getattr(module, attr)
@@ -140,7 +143,7 @@ def javascript_catalog(request, domain='djangojs', packages=None):
activate(request.GET['language'])
if packages is None:
packages = ['django.conf']
- if type(packages) in (str, unicode):
+ if isinstance(packages, basestring):
packages = packages.split('+')
packages = [p for p in packages if p == 'django.conf' or p in settings.INSTALLED_APPS]
default_locale = to_locale(settings.LANGUAGE_CODE)
@@ -194,9 +197,9 @@ def javascript_catalog(request, domain='djangojs', packages=None):
for k, v in t.items():
if k == '':
continue
- if type(k) in (str, unicode):
+ if isinstance(k, basestring):
csrc.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(v)))
- elif type(k) == tuple:
+ elif isinstance(k, tuple):
if k[0] not in pdict:
pdict[k[0]] = k[1]
else:
@@ -208,7 +211,11 @@ def javascript_catalog(request, domain='djangojs', packages=None):
for k, v in pdict.items():
src.append("catalog['%s'] = [%s];\n" % (javascript_quote(k), ','.join(["''"]*(v+1))))
for k, v in get_formats().items():
- src.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(unicode(v))))
+ if isinstance(v, (basestring, int)):
+ src.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(smart_unicode(v))))
+ elif isinstance(v, (tuple, list)):
+ v = [javascript_quote(smart_unicode(value)) for value in v]
+ src.append("catalog['%s'] = ['%s'];\n" % (javascript_quote(k), "', '".join(v)))
src.extend(csrc)
src.append(LibFoot)
src.append(InterPolate)
diff --git a/docs/ref/contrib/localflavor.txt b/docs/ref/contrib/localflavor.txt
index 660b7c623b..1c58e2d5e8 100644
--- a/docs/ref/contrib/localflavor.txt
+++ b/docs/ref/contrib/localflavor.txt
@@ -50,13 +50,17 @@ Countries currently supported by :mod:`~django.contrib.localflavor` are:
* Germany_
* Iceland_
* India_
+ * Indonesia_
+ * Ireland_
* Italy_
* Japan_
+ * Kuwait_
* Mexico_
* `The Netherlands`_
* Norway_
* Peru_
* Poland_
+ * Portugal_
* Romania_
* Slovakia_
* `South Africa`_
@@ -65,6 +69,7 @@ Countries currently supported by :mod:`~django.contrib.localflavor` are:
* Switzerland_
* `United Kingdom`_
* `United States of America`_
+ * Uruguay_
The ``django.contrib.localflavor`` package also includes a ``generic`` subpackage,
containing useful code that is not specific to one particular country or culture.
@@ -92,12 +97,16 @@ Here's an example of how to use them::
.. _The Netherlands: `The Netherlands (nl)`_
.. _Iceland: `Iceland (is\_)`_
.. _India: `India (in\_)`_
+.. _Indonesia: `Indonesia (id)`_
+.. _Ireland: `Ireland (ie)`_
.. _Italy: `Italy (it)`_
.. _Japan: `Japan (jp)`_
+.. _Kuwait: `Kuwait (kw)`_
.. _Mexico: `Mexico (mx)`_
.. _Norway: `Norway (no)`_
.. _Peru: `Peru (pe)`_
.. _Poland: `Poland (pl)`_
+.. _Portugal: `Portugal (pt)`_
.. _Romania: `Romania (ro)`_
.. _Slovakia: `Slovakia (sk)`_
.. _South Africa: `South Africa (za)`_
@@ -106,6 +115,7 @@ Here's an example of how to use them::
.. _Switzerland: `Switzerland (ch)`_
.. _United Kingdom: `United Kingdom (uk)`_
.. _United States of America: `United States of America (us)`_
+.. _Uruguay: `Uruguay (uy)`_
Adding flavors
==============
@@ -369,6 +379,46 @@ India (``in_``)
A ``Select`` widget that uses a list of Indian states/territories as its
choices.
+Ireland (``ie``)
+================
+
+.. class:: ie.forms.IECountySelect
+
+ A ``Select`` widget that uses a list of Irish Counties as its choices.
+
+Indonesia (``id``)
+==================
+
+.. class:: id.forms.IDPostCodeField
+
+ A form field that validates input as an Indonesian post code field.
+
+.. class:: id.forms.IDProvinceSelect
+
+ A ``Select`` widget that uses a list of Indonesian provinces as its choices.
+
+.. class:: id.forms.IDPhoneNumberField
+
+ A form field that validates input as an Indonesian telephone number.
+
+.. class:: id.forms.IDLicensePlatePrefixSelect
+
+ A ``Select`` widget that uses a list of Indonesian license plate
+ prefix code as its choices.
+
+.. class:: id.forms.IDLicensePlateField
+
+ A form field that validates input as an Indonesian vehicle license plate.
+
+.. class:: id.forms.IDNationalIdentityNumberField
+
+ A form field that validates input as an Indonesian national identity
+ number (`NIK`_/KTP). The output will be in the format of
+ 'XX.XXXX.DDMMYY.XXXX'. Dots or spaces can be used in the input to break
+ down the numbers.
+
+.. _NIK: http://en.wikipedia.org/wiki/Indonesian_identity_card
+
Italy (``it``)
==============
@@ -408,6 +458,18 @@ Japan (``jp``)
A ``Select`` widget that uses a list of Japanese prefectures as its choices.
+Kuwait (``kw``)
+===============
+
+.. class:: kw.forms.KWCivilIDNumberField
+
+ A form field that validates input as a Kuwaiti Civil ID number. A valid
+ Civil ID number must obey the following rules:
+
+ * 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.
+
Mexico (``mx``)
===============
@@ -438,31 +500,31 @@ Norway (``no``)
Peru (``pe``)
=============
-.. class:: pt.forms.PEDNIField
+.. class:: pe.forms.PEDNIField
A form field that validates input as a DNI (Peruvian national identity)
number.
-.. class:: pt.forms.PERUCField
+.. class:: pe.forms.PERUCField
A form field that validates input as an RUC (Registro Unico de
Contribuyentes) number. Valid RUC numbers have 11 digits.
-.. class:: pt.forms.PEDepartmentSelect
+.. class:: pe.forms.PEDepartmentSelect
A ``Select`` widget that uses a list of Peruvian Departments as its choices.
Poland (``pl``)
===============
-.. class:: pl.forms.PLNationalIdentificationNumberField
+.. class:: pl.forms.PLPESELField
A form field that validates input as a Polish national identification number
(PESEL_).
.. _PESEL: http://en.wikipedia.org/wiki/PESEL
-.. class:: pl.forms.PLNationalBusinessRegisterField
+.. class:: pl.forms.PLREGONField
A form field that validates input as a Polish National Official Business
Register Number (REGON_), having either seven or nine digits. The checksum
@@ -476,22 +538,35 @@ Poland (``pl``)
A form field that validates input as a Polish postal code. The valid format
is XX-XXX, where X is a digit.
-.. class:: pl.forms.PLTaxNumberField
+.. class:: pl.forms.PLNIPField
A form field that validates input as a Polish Tax Number (NIP). Valid
formats are XXX-XXX-XX-XX or XX-XX-XXX-XXX. The checksum algorithm used
for NIPs is documented at http://wipos.p.lodz.pl/zylla/ut/nip-rego.html.
-.. class:: pl.forms.PLAdministrativeUnitSelect
+.. class:: pl.forms.PLCountySelect
A ``Select`` widget that uses a list of Polish administrative units as its
choices.
-.. class:: pl.forms.PLVoivodeshipSelect
+.. class:: pl.forms.PLProvinceSelect
A ``Select`` widget that uses a list of Polish voivodeships (administrative
provinces) as its choices.
+Portugal (``pt``)
+=================
+
+.. class:: pt.forms.PTZipCodeField
+
+ A form field that validates input as a Portuguese zip code.
+
+.. class:: pt.forms.PTPhoneNumberField
+
+ A form field that validates input as a Portuguese phone number.
+ Valid numbers have 9 digits (may include spaces) or start by 00
+ or + (international).
+
Romania (``ro``)
================
@@ -738,3 +813,15 @@ United States of America (``us``)
A model field that forms represent as a ``forms.USStateField`` field and
stores the two-letter U.S. state abbreviation in the database.
+
+Uruguay (``uy``)
+================
+
+.. class:: uy.forms.UYCIField
+
+ A field that validates Uruguayan 'Cedula de identidad' (CI) numbers.
+
+.. class:: uy.forms.UYDepartamentSelect
+
+ A ``Select`` widget that uses a list of Uruguayan departaments as its
+ choices.
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index 2cd879423c..88715ebdc7 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -812,12 +812,10 @@ test <app or test identifier>
Runs tests for all installed models. See :ref:`topics-testing` for more
information.
---failfast
-~~~~~~~~~~
-
.. versionadded:: 1.2
+.. django-admin-option:: --failfast
-Use the ``--failfast`` option to stop running tests and report the failure
+Use the :djadminopt:`--failfast` option to stop running tests and report the failure
immediately after a test fails.
testserver <fixture fixture ...>
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index 8cbc9ccc8f..daedc87d21 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -219,7 +219,7 @@ SQLite. This can be configured using the following::
DATABASES = {
'default': {
- 'BACKEND': 'django.db.backends.sqlite3',
+ 'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'mydatabase'
}
}
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index b7afa15f7f..51ef03c886 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -637,6 +637,7 @@ Available format strings:
A ``'AM'`` or ``'PM'``. ``'AM'``
b Month, textual, 3 letters, lowercase. ``'jan'``
B Not implemented.
+ c ISO 8601 Format. ``2008-01-02 10:30:00.000123``
d Day of the month, 2 digits with ``'01'`` to ``'31'``
leading zeros.
D Day of the week, textual, 3 letters. ``'Fri'``
@@ -673,6 +674,7 @@ Available format strings:
month, 2 characters.
t Number of days in the given month. ``28`` to ``31``
T Time zone of this machine. ``'EST'``, ``'MDT'``
+ u Microseconds. ``0`` to ``999999``
U Seconds since the Unix Epoch
(January 1 1970 00:00:00 UTC).
w Day of the week, digits without ``'0'`` (Sunday) to ``'6'`` (Saturday)
diff --git a/docs/releases/1.1.2.txt b/docs/releases/1.1.2.txt
index 64bbdd5dde..4c887ad4a5 100644
--- a/docs/releases/1.1.2.txt
+++ b/docs/releases/1.1.2.txt
@@ -19,8 +19,21 @@ development or deployment currently using or targeting Django 1.1.
For full details on the new features, backwards incompatibilities, and
deprecated features in the 1.1 branch, see the :ref:`releases-1.1`.
+Backwards-incompatible changes in 1.1.2
+=======================================
+
+Test runner exit status code
+----------------------------
+
+The exit status code of the test runners (``tests/runtests.py`` and ``python
+manage.py test``) no longer represents the number of failed tests, since a
+failure of 256 or more tests resulted in a wrong exit status code. The exit
+status code for the test runner is now 0 for success (no failing tests) and 1
+for any number of test failures. If needed, the number of test failures can be
+found at the end of the test runner's output.
+
One new feature
----------------
+===============
Ordinarily, a point release would not include new features, but in the
case of Django 1.1.2, we have made an exception to this rule. Django
diff --git a/docs/releases/1.2.txt b/docs/releases/1.2.txt
index 8810963eaa..a549bd0cce 100644
--- a/docs/releases/1.2.txt
+++ b/docs/releases/1.2.txt
@@ -232,6 +232,16 @@ party packages, or that you wrote yourself, you should ensure that the
information, see
:ref:`template tag thread safety considerations<template_tag_thread_safety>`.
+Test runner exit status code
+----------------------------
+
+The exit status code of the test runners (``tests/runtests.py`` and ``python
+manage.py test``) no longer represents the number of failed tests, since a
+failure of 256 or more tests resulted in a wrong exit status code. The exit
+status code for the test runner is now 0 for success (no failing tests) and 1
+for any number of test failures. If needed, the number of test failures can be
+found at the end of the test runner's output.
+
.. _deprecated-features-1.2:
Features deprecated in 1.2
@@ -471,10 +481,12 @@ Models can now use a 64 bit :class:`~django.db.models.BigIntegerField` type.
Fast Failure for Tests
----------------------
-The ``test`` subcommand of ``django-admin.py``, and the ``runtests.py`` script
-used to run Django's own test suite, support a new ``--failfast`` option.
-When specified, this option causes the test runner to exit after
-encountering a failure instead of continuing with the test run.
+The :djadmin:`test` subcommand of ``django-admin.py``, and the ``runtests.py``
+script used to run Django's own test suite, support a new ``--failfast`` option.
+When specified, this option causes the test runner to exit after encountering
+a failure instead of continuing with the test run. In addition, the handling
+of ``Ctrl-C`` during a test run has been improved to trigger a graceful exit
+from the test run that reports details of the tests run before the interruption.
Improved localization
---------------------
diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt
index 8aae4f5e73..9874135b02 100644
--- a/docs/topics/auth.txt
+++ b/docs/topics/auth.txt
@@ -34,7 +34,7 @@ Authentication support is bundled as a Django application in
1. Put ``'django.contrib.auth'`` and ``'django.contrib.contenttypes'`` in
your :setting:`INSTALLED_APPS` setting.
- (The :class:`~django.contrib.auth.models.Permisson` model in
+ (The :class:`~django.contrib.auth.models.Permission` model in
:mod:`django.contrib.auth` depends on :mod:`django.contrib.contenttypes`.)
2. Run the command ``manage.py syncdb``.
diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt
index 9ef6cceae8..a309294d7c 100644
--- a/docs/topics/testing.txt
+++ b/docs/topics/testing.txt
@@ -242,8 +242,8 @@ in different circumstances.
Running tests
=============
-Once you've written tests, run them using your project's ``manage.py``
-utility::
+Once you've written tests, run them using the :djadmin:`test` subcommand of
+your project's ``manage.py`` utility::
$ ./manage.py test
@@ -274,6 +274,23 @@ a test case, add the name of the test method to the label::
$ ./manage.py test animals.AnimalTestCase.testFluffyAnimals
+.. versionadded:: 1.2
+ You can now trigger a graceful exit from a test run by pressing ``Ctrl-C``.
+
+If you press ``Ctrl-C`` while the tests are running, the test runner will
+wait for the currently running test to complete and then exit gracefully.
+During a graceful exit the test runner will output details of any test
+failures, report on how many tests were run and how many errors and failures
+were encountered, and destroy any test databases as usual. Thus pressing
+``Ctrl-C`` can be very useful if you forget to pass the :djadminopt:`--failfast`
+option, notice that some tests are unexpectedly failing, and want to get details
+on the failures without waiting for the full test run to complete.
+
+If you do not want to wait for the currently running test to finish, you
+can press ``Ctrl-C`` a second time and the test run will halt immediately,
+but not gracefully. No details of the tests run before the interruption will
+be reported, and any test databases created by the run will not be destroyed.
+
The test database
-----------------
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index 320b632537..df43701586 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -63,11 +63,20 @@ class AdminViewBasicTest(TestCase):
def testBasicEditGet(self):
"""
- A smoke test to ensureGET on the change_view works.
+ A smoke test to ensure GET on the change_view works.
"""
response = self.client.get('/test_admin/%s/admin_views/section/1/' % self.urlbit)
self.failUnlessEqual(response.status_code, 200)
+ def testBasicEditGetStringPK(self):
+ """
+ A smoke test to ensure GET on the change_view works (returns an HTTP
+ 404 error, see #11191) when passing a string as the PK argument for a
+ model with an integer PK field.
+ """
+ response = self.client.get('/test_admin/%s/admin_views/section/abc/' % self.urlbit)
+ self.failUnlessEqual(response.status_code, 404)
+
def testBasicAddPost(self):
"""
A smoke test to ensure POST on add_view works.
diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py
index 0c81ed3119..b9fe67c182 100644
--- a/tests/regressiontests/admin_widgets/models.py
+++ b/tests/regressiontests/admin_widgets/models.py
@@ -77,6 +77,9 @@ __test__ = {'WIDGETS_TESTS': """
>>> from django.contrib.admin.widgets import FilteredSelectMultiple, AdminSplitDateTime
>>> from django.contrib.admin.widgets import AdminFileWidget, ForeignKeyRawIdWidget, ManyToManyRawIdWidget
>>> from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
+>>> from django.utils.translation import activate, deactivate
+>>> from django.conf import settings
+
Calling conditional_escape on the output of widget.render will simulate what
happens in the template. This is easier than setting up a template and context
@@ -94,6 +97,12 @@ HTML escaped.
>>> w = AdminSplitDateTime()
>>> print conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30)))
<p class="datetime">Date: <input value="2007-12-01" type="text" class="vDateField" name="test_0" size="10" /><br />Time: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>
+>>> activate('de-at')
+>>> settings.USE_L10N = True
+>>> print conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30)))
+<p class="datetime">Datum: <input value="01.12.2007" type="text" class="vDateField" name="test_0" size="10" /><br />Zeit: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>
+>>> deactivate()
+>>> settings.USE_L10N = False
>>> band = Band.objects.create(pk=1, name='Linkin Park')
>>> album = band.album_set.create(name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg')
diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
index 85651542bb..5601b49193 100644
--- a/tests/regressiontests/admin_widgets/tests.py
+++ b/tests/regressiontests/admin_widgets/tests.py
@@ -3,6 +3,7 @@ from django.contrib import admin
from django.contrib.admin import widgets
from unittest import TestCase
from django.test import TestCase as DjangoTestCase
+from django.db.models import DateField
import models
class AdminFormfieldForDBFieldTests(TestCase):
@@ -89,7 +90,7 @@ class AdminFormfieldForDBFieldTests(TestCase):
def testFormfieldOverrides(self):
self.assertFormfield(models.Event, 'start_date', forms.TextInput,
- formfield_overrides={'widget': forms.TextInput})
+ formfield_overrides={DateField: {'widget': forms.TextInput}})
def testFieldWithChoices(self):
self.assertFormfield(models.Member, 'gender', forms.Select)
diff --git a/tests/regressiontests/datastructures/tests.py b/tests/regressiontests/datastructures/tests.py
index e658f3302e..b7885e65a4 100644
--- a/tests/regressiontests/datastructures/tests.py
+++ b/tests/regressiontests/datastructures/tests.py
@@ -60,9 +60,9 @@ MultiValueDictKeyError: "Key 'lastname' not found in <MultiValueDict: {'position
>>> d.setlist('lastname', ['Holovaty', 'Willison'])
>>> d.getlist('lastname')
['Holovaty', 'Willison']
->>> d.values()
+>>> d.values()
['Developer', 'Simon', 'Willison']
->>> list(d.itervalues())
+>>> list(d.itervalues())
['Developer', 'Simon', 'Willison']
### SortedDict #################################################################
@@ -95,6 +95,9 @@ True
>>> d.pop('one', 'missing')
'missing'
+>>> SortedDict((i, i) for i in xrange(3))
+{0: 0, 1: 1, 2: 2}
+
We don't know which item will be popped in popitem(), so we'll just check that
the number of keys has decreased.
>>> l = len(d)
diff --git a/tests/regressiontests/dateformat/tests.py b/tests/regressiontests/dateformat/tests.py
index bbde45b903..42c582cd0f 100644
--- a/tests/regressiontests/dateformat/tests.py
+++ b/tests/regressiontests/dateformat/tests.py
@@ -39,8 +39,10 @@ class DateFormatTests(TestCase):
def test_date_formats(self):
my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
+ timestamp = datetime.datetime(2008, 5, 19, 11, 45, 23, 123456)
self.assertEquals(dateformat.format(my_birthday, 'A'), u'PM')
+ self.assertEquals(dateformat.format(timestamp, 'c'), u'2008-05-19 11:45:23.123456')
self.assertEquals(dateformat.format(my_birthday, 'd'), u'08')
self.assertEquals(dateformat.format(my_birthday, 'j'), u'8')
self.assertEquals(dateformat.format(my_birthday, 'l'), u'Sunday')
@@ -79,12 +81,14 @@ class DateFormatTests(TestCase):
my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
summertime = datetime.datetime(2005, 10, 30, 1, 00)
wintertime = datetime.datetime(2005, 10, 30, 4, 00)
+ timestamp = datetime.datetime(2008, 5, 19, 11, 45, 23, 123456)
if self.tz_tests:
self.assertEquals(dateformat.format(my_birthday, 'O'), u'+0100')
self.assertEquals(dateformat.format(my_birthday, 'r'), u'Sun, 8 Jul 1979 22:00:00 +0100')
self.assertEquals(dateformat.format(my_birthday, 'T'), u'CET')
self.assertEquals(dateformat.format(my_birthday, 'U'), u'300315600')
+ self.assertEquals(dateformat.format(timestamp, 'u'), u'123456')
self.assertEquals(dateformat.format(my_birthday, 'Z'), u'3600')
self.assertEquals(dateformat.format(summertime, 'I'), u'1')
self.assertEquals(dateformat.format(summertime, 'O'), u'+0200')
diff --git a/tests/regressiontests/forms/localflavor/id.py b/tests/regressiontests/forms/localflavor/id.py
new file mode 100644
index 0000000000..9098b9d6c0
--- /dev/null
+++ b/tests/regressiontests/forms/localflavor/id.py
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+# Tests for the contrib/localflavor/ ID form fields.
+
+tests = r"""
+
+# IDPhoneNumberField ########################################################
+
+>>> from django.contrib.localflavor.id.forms import IDPhoneNumberField
+>>> f = IDPhoneNumberField(required=False)
+>>> f.clean('')
+u''
+>>> f.clean('0812-3456789')
+u'0812-3456789'
+>>> f.clean('081234567890')
+u'081234567890'
+>>> f.clean('021 345 6789')
+u'021 345 6789'
+>>> f.clean('0213456789')
+u'0213456789'
+>>> f.clean('0123456789')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid phone number']
+>>> f.clean('+62-21-3456789')
+u'+62-21-3456789'
+>>> f.clean('+62-021-3456789')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid phone number']
+>>> f.clean('(021) 345 6789')
+u'(021) 345 6789'
+>>> f.clean('+62-021-3456789')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid phone number']
+>>> f.clean('+62-0812-3456789')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid phone number']
+>>> f.clean('0812345678901')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid phone number']
+>>> f.clean('foo')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid phone number']
+
+# IDPostCodeField ############################################################
+
+>>> from django.contrib.localflavor.id.forms import IDPostCodeField
+>>> f = IDPostCodeField(required=False)
+>>> f.clean('')
+u''
+>>> f.clean('12340')
+u'12340'
+>>> f.clean('25412')
+u'25412'
+>>> f.clean(' 12340 ')
+u'12340'
+>>> f.clean('12 3 4 0')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid post code']
+>>> f.clean('12345')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid post code']
+>>> f.clean('10100')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid post code']
+>>> f.clean('123456')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid post code']
+>>> f.clean('foo')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid post code']
+
+# IDNationalIdentityNumberField #########################################################
+
+>>> from django.contrib.localflavor.id.forms import IDNationalIdentityNumberField
+>>> f = IDNationalIdentityNumberField(required=False)
+>>> f.clean('')
+u''
+>>> f.clean(' 12.3456.010178 3456 ')
+u'12.3456.010178.3456'
+>>> f.clean('1234560101783456')
+u'12.3456.010178.3456'
+>>> f.clean('12.3456.010101.3456')
+u'12.3456.010101.3456'
+>>> f.clean('12.3456.310278.3456')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid NIK/KTP number']
+>>> f.clean('00.0000.010101.0000')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid NIK/KTP number']
+>>> f.clean('1234567890123456')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid NIK/KTP number']
+>>> f.clean('foo')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid NIK/KTP number']
+
+# IDProvinceSelect ##########################################################
+
+>>> from django.contrib.localflavor.id.forms import IDProvinceSelect
+>>> s = IDProvinceSelect()
+>>> s.render('provinces', 'LPG')
+u'<select name="provinces">\n<option value="BLI">Bali</option>\n<option value="BTN">Banten</option>\n<option value="BKL">Bengkulu</option>\n<option value="DIY">Yogyakarta</option>\n<option value="JKT">Jakarta</option>\n<option value="GOR">Gorontalo</option>\n<option value="JMB">Jambi</option>\n<option value="JBR">Jawa Barat</option>\n<option value="JTG">Jawa Tengah</option>\n<option value="JTM">Jawa Timur</option>\n<option value="KBR">Kalimantan Barat</option>\n<option value="KSL">Kalimantan Selatan</option>\n<option value="KTG">Kalimantan Tengah</option>\n<option value="KTM">Kalimantan Timur</option>\n<option value="BBL">Kepulauan Bangka-Belitung</option>\n<option value="KRI">Kepulauan Riau</option>\n<option value="LPG" selected="selected">Lampung</option>\n<option value="MLK">Maluku</option>\n<option value="MUT">Maluku Utara</option>\n<option value="NAD">Nanggroe Aceh Darussalam</option>\n<option value="NTB">Nusa Tenggara Barat</option>\n<option value="NTT">Nusa Tenggara Timur</option>\n<option value="PPA">Papua</option>\n<option value="PPB">Papua Barat</option>\n<option value="RIU">Riau</option>\n<option value="SLB">Sulawesi Barat</option>\n<option value="SLS">Sulawesi Selatan</option>\n<option value="SLT">Sulawesi Tengah</option>\n<option value="SLR">Sulawesi Tenggara</option>\n<option value="SLU">Sulawesi Utara</option>\n<option value="SMB">Sumatera Barat</option>\n<option value="SMS">Sumatera Selatan</option>\n<option value="SMU">Sumatera Utara</option>\n</select>'
+
+# IDLicensePlatePrefixelect ########################################################################
+
+>>> from django.contrib.localflavor.id.forms import IDLicensePlatePrefixSelect
+>>> s = IDLicensePlatePrefixSelect()
+>>> s.render('codes', 'BE')
+u'<select name="codes">\n<option value="A">Banten</option>\n<option value="AA">Magelang</option>\n<option value="AB">Yogyakarta</option>\n<option value="AD">Surakarta - Solo</option>\n<option value="AE">Madiun</option>\n<option value="AG">Kediri</option>\n<option value="B">Jakarta</option>\n<option value="BA">Sumatera Barat</option>\n<option value="BB">Tapanuli</option>\n<option value="BD">Bengkulu</option>\n<option value="BE" selected="selected">Lampung</option>\n<option value="BG">Sumatera Selatan</option>\n<option value="BH">Jambi</option>\n<option value="BK">Sumatera Utara</option>\n<option value="BL">Nanggroe Aceh Darussalam</option>\n<option value="BM">Riau</option>\n<option value="BN">Kepulauan Bangka Belitung</option>\n<option value="BP">Kepulauan Riau</option>\n<option value="CC">Corps Consulate</option>\n<option value="CD">Corps Diplomatic</option>\n<option value="D">Bandung</option>\n<option value="DA">Kalimantan Selatan</option>\n<option value="DB">Sulawesi Utara Daratan</option>\n<option value="DC">Sulawesi Barat</option>\n<option value="DD">Sulawesi Selatan</option>\n<option value="DE">Maluku</option>\n<option value="DG">Maluku Utara</option>\n<option value="DH">NTT - Timor</option>\n<option value="DK">Bali</option>\n<option value="DL">Sulawesi Utara Kepulauan</option>\n<option value="DM">Gorontalo</option>\n<option value="DN">Sulawesi Tengah</option>\n<option value="DR">NTB - Lombok</option>\n<option value="DS">Papua dan Papua Barat</option>\n<option value="DT">Sulawesi Tenggara</option>\n<option value="E">Cirebon</option>\n<option value="EA">NTB - Sumbawa</option>\n<option value="EB">NTT - Flores</option>\n<option value="ED">NTT - Sumba</option>\n<option value="F">Bogor</option>\n<option value="G">Pekalongan</option>\n<option value="H">Semarang</option>\n<option value="K">Pati</option>\n<option value="KB">Kalimantan Barat</option>\n<option value="KH">Kalimantan Tengah</option>\n<option value="KT">Kalimantan Timur</option>\n<option value="L">Surabaya</option>\n<option value="M">Madura</option>\n<option value="N">Malang</option>\n<option value="P">Jember</option>\n<option value="R">Banyumas</option>\n<option value="RI">Federal Government</option>\n<option value="S">Bojonegoro</option>\n<option value="T">Purwakarta</option>\n<option value="W">Sidoarjo</option>\n<option value="Z">Garut</option>\n</select>'
+
+# IDLicensePlateField #######################################################################
+
+>>> from django.contrib.localflavor.id.forms import IDLicensePlateField
+>>> f = IDLicensePlateField(required=False)
+>>> f.clean('')
+u''
+>>> f.clean(' b 1234 ab ')
+u'B 1234 AB'
+>>> f.clean('B 1234 ABC')
+u'B 1234 ABC'
+>>> f.clean('A 12')
+u'A 12'
+>>> f.clean('DK 12345 12')
+u'DK 12345 12'
+>>> f.clean('RI 10')
+u'RI 10'
+>>> f.clean('CD 12 12')
+u'CD 12 12'
+>>> f.clean('CD 10 12')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid vehicle license plate number']
+>>> f.clean('CD 1234 12')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid vehicle license plate number']
+>>> f.clean('RI 10 AB')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid vehicle license plate number']
+>>> f.clean('B 12345 01')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid vehicle license plate number']
+>>> f.clean('N 1234 12')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid vehicle license plate number']
+>>> f.clean('A 12 XYZ')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid vehicle license plate number']
+>>> f.clean('Q 1234 AB')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid vehicle license plate number']
+>>> f.clean('foo')
+Traceback (most recent call last):
+ ...
+ValidationError: [u'Enter a valid vehicle license plate number']
+""" \ No newline at end of file
diff --git a/tests/regressiontests/forms/localflavor/ie.py b/tests/regressiontests/forms/localflavor/ie.py
new file mode 100644
index 0000000000..540281c19e
--- /dev/null
+++ b/tests/regressiontests/forms/localflavor/ie.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Tests for the contrib/localflavor/ie form fields.
+
+tests = r"""
+# IECountySelect #########################################################
+
+>>> from django.contrib.localflavor.ie.forms import IECountySelect
+>>> f = IECountySelect()
+>>> f.render('counties', 'dublin')
+u'<select name="counties">\n<option value="antrim">Antrim</option>\n<option value="armagh">Armagh</option>\n<option value="carlow">Carlow</option>\n<option value="cavan">Cavan</option>\n<option value="clare">Clare</option>\n<option value="cork">Cork</option>\n<option value="derry">Derry</option>\n<option value="donegal">Donegal</option>\n<option value="down">Down</option>\n<option value="dublin" selected="selected">Dublin</option>\n<option value="fermanagh">Fermanagh</option>\n<option value="galway">Galway</option>\n<option value="kerry">Kerry</option>\n<option value="kildare">Kildare</option>\n<option value="kilkenny">Kilkenny</option>\n<option value="laois">Laois</option>\n<option value="leitrim">Leitrim</option>\n<option value="limerick">Limerick</option>\n<option value="longford">Longford</option>\n<option value="louth">Louth</option>\n<option value="mayo">Mayo</option>\n<option value="meath">Meath</option>\n<option value="monaghan">Monaghan</option>\n<option value="offaly">Offaly</option>\n<option value="roscommon">Roscommon</option>\n<option value="sligo">Sligo</option>\n<option value="tipperary">Tipperary</option>\n<option value="tyrone">Tyrone</option>\n<option value="waterford">Waterford</option>\n<option value="westmeath">Westmeath</option>\n<option value="wexford">Wexford</option>\n<option value="wicklow">Wicklow</option>\n</select>'
+
+"""
diff --git a/tests/regressiontests/forms/localflavor/kw.py b/tests/regressiontests/forms/localflavor/kw.py
new file mode 100644
index 0000000000..265c1f00ae
--- /dev/null
+++ b/tests/regressiontests/forms/localflavor/kw.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+# Tests for the contrib/localflavor/ KW form fields.
+
+tests = r"""
+# KWCivilIDNumberField ########################################################
+
+>>> from django.contrib.localflavor.kw.forms import KWCivilIDNumberField
+>>> f = KWCivilIDNumberField()
+>>> f.clean('282040701483')
+'282040701483'
+>>> f.clean('289332013455')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Kuwaiti Civil ID number']
+"""
diff --git a/tests/regressiontests/forms/localflavor/pt.py b/tests/regressiontests/forms/localflavor/pt.py
new file mode 100644
index 0000000000..56d4d54c02
--- /dev/null
+++ b/tests/regressiontests/forms/localflavor/pt.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+# Tests for the contrib/localflavor/ PT form fields.
+
+tests = r"""
+# PTZipCodeField #############################################################
+
+PTZipCodeField validates that the data is a valid PT zipcode.
+>>> from django.contrib.localflavor.pt.forms import PTZipCodeField
+>>> f = PTZipCodeField()
+>>> f.clean('3030-034')
+u'3030-034'
+>>> f.clean('1003456')
+u'1003-456'
+>>> f.clean('2A200')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a zip code in the format XXXX-XXX.']
+>>> f.clean('980001')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a zip code in the format XXXX-XXX.']
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f = PTZipCodeField(required=False)
+>>> f.clean('3030-034')
+u'3030-034'
+>>> f.clean('1003456')
+u'1003-456'
+>>> f.clean('2A200')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a zip code in the format XXXX-XXX.']
+>>> f.clean('980001')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a zip code in the format XXXX-XXX.']
+>>> f.clean(None)
+u''
+>>> f.clean('')
+u''
+
+# PTPhoneNumberField ##########################################################
+
+PTPhoneNumberField validates that the data is a valid Portuguese phone number.
+It's normalized to XXXXXXXXX format or +X(X) for international numbers. Dots are valid too.
+>>> from django.contrib.localflavor.pt.forms import PTPhoneNumberField
+>>> f = PTPhoneNumberField()
+>>> f.clean('917845189')
+u'917845189'
+>>> f.clean('91 784 5189')
+u'917845189'
+>>> f.clean('91 784 5189')
+u'917845189'
+>>> f.clean('+351 91 111')
+u'+35191111'
+>>> f.clean('00351873')
+u'00351873'
+>>> f.clean('91 784 51 8')
+Traceback (most recent call last):
+...
+ValidationError: [u'Phone numbers must have 9 digits, or start by + or 00.']
+>>> f.clean('091 456 987 1')
+Traceback (most recent call last):
+...
+ValidationError: [u'Phone numbers must have 9 digits, or start by + or 00.']
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f = PTPhoneNumberField(required=False)
+>>> f.clean('917845189')
+u'917845189'
+>>> f.clean('91 784 5189')
+u'917845189'
+>>> f.clean('91 784 5189')
+u'917845189'
+>>> f.clean('+351 91 111')
+u'+35191111'
+>>> f.clean('00351873')
+u'00351873'
+>>> f.clean('91 784 51 8')
+Traceback (most recent call last):
+...
+ValidationError: [u'Phone numbers must have 9 digits, or start by + or 00.']
+>>> f.clean('091 456 987 1')
+Traceback (most recent call last):
+...
+ValidationError: [u'Phone numbers must have 9 digits, or start by + or 00.']
+>>> f.clean(None)
+u''
+>>> f.clean('')
+u''
+
+"""
diff --git a/tests/regressiontests/forms/localflavor/uk.py b/tests/regressiontests/forms/localflavor/uk.py
index 258c22e5a9..cd6c8edf84 100644
--- a/tests/regressiontests/forms/localflavor/uk.py
+++ b/tests/regressiontests/forms/localflavor/uk.py
@@ -58,4 +58,15 @@ u'BT32 4PX'
u''
>>> f.clean('')
u''
+>>> class MyUKPostcodeField(UKPostcodeField):
+... default_error_messages = {
+... 'invalid': 'Enter a bloody postcode!',
+... }
+...
+>>>
+>>> f = MyUKPostcodeField(required=False)
+>>> f.clean('1NV 4L1D')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a bloody postcode!']
"""
diff --git a/tests/regressiontests/forms/localflavor/uy.py b/tests/regressiontests/forms/localflavor/uy.py
new file mode 100644
index 0000000000..057ab193f5
--- /dev/null
+++ b/tests/regressiontests/forms/localflavor/uy.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Tests for the contrib/localflavor/ UY form fields.
+
+tests = r"""
+# UYDepartamentSelect #########################################################
+
+>>> from django.contrib.localflavor.uy.forms import UYDepartamentSelect
+>>> f = UYDepartamentSelect()
+>>> f.render('departamentos', 'S')
+u'<select name="departamentos">\n<option value="G">Artigas</option>\n<option value="A">Canelones</option>\n<option value="E">Cerro Largo</option>\n<option value="L">Colonia</option>\n<option value="Q">Durazno</option>\n<option value="N">Flores</option>\n<option value="O">Florida</option>\n<option value="P">Lavalleja</option>\n<option value="B">Maldonado</option>\n<option value="S" selected="selected">Montevideo</option>\n<option value="I">Paysand\xfa</option>\n<option value="J">R\xedo Negro</option>\n<option value="F">Rivera</option>\n<option value="C">Rocha</option>\n<option value="H">Salto</option>\n<option value="M">San Jos\xe9</option>\n<option value="K">Soriano</option>\n<option value="R">Tacuaremb\xf3</option>\n<option value="D">Treinta y Tres</option>\n</select>'
+
+# UYCIField ###################################################################
+
+>>> from django.contrib.localflavor.uy.util import get_validation_digit
+>>> get_validation_digit(409805) == 3
+True
+>>> get_validation_digit(1005411) == 2
+True
+
+>>> from django.contrib.localflavor.uy.forms import UYCIField
+>>> f = UYCIField()
+>>> f.clean('4098053')
+u'4098053'
+>>> f.clean('409805-3')
+u'409805-3'
+>>> f.clean('409.805-3')
+u'409.805-3'
+>>> f.clean('10054112')
+u'10054112'
+>>> f.clean('1005411-2')
+u'1005411-2'
+>>> f.clean('1.005.411-2')
+u'1.005.411-2'
+>>> f.clean('foo')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid CI number in X.XXX.XXX-X,XXXXXXX-X or XXXXXXXX format.']
+>>> f.clean('409805-2')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid CI number.']
+>>> f.clean('1.005.411-5')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid CI number.']
+"""
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index b6b7752f5a..e9ae948dfd 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -15,16 +15,21 @@ from localflavor.es import tests as localflavor_es_tests
from localflavor.fi import tests as localflavor_fi_tests
from localflavor.fr import tests as localflavor_fr_tests
from localflavor.generic import tests as localflavor_generic_tests
+from localflavor.id import tests as localflavor_id_tests
+from localflavor.ie import tests as localflavor_ie_tests
from localflavor.is_ import tests as localflavor_is_tests
from localflavor.it import tests as localflavor_it_tests
from localflavor.jp import tests as localflavor_jp_tests
+from localflavor.kw import tests as localflavor_kw_tests
from localflavor.nl import tests as localflavor_nl_tests
from localflavor.pl import tests as localflavor_pl_tests
+from localflavor.pt import tests as localflavor_pt_tests
from localflavor.ro import tests as localflavor_ro_tests
from localflavor.se import tests as localflavor_se_tests
from localflavor.sk import tests as localflavor_sk_tests
from localflavor.uk import tests as localflavor_uk_tests
from localflavor.us import tests as localflavor_us_tests
+from localflavor.uy import tests as localflavor_uy_tests
from localflavor.za import tests as localflavor_za_tests
from regressions import tests as regression_tests
from util import tests as util_tests
@@ -52,16 +57,21 @@ __test__ = {
'localflavor_fi_tests': localflavor_fi_tests,
'localflavor_fr_tests': localflavor_fr_tests,
'localflavor_generic_tests': localflavor_generic_tests,
+ 'localflavor_id_tests': localflavor_id_tests,
+ 'localflavor_ie_tests': localflavor_ie_tests,
'localflavor_is_tests': localflavor_is_tests,
'localflavor_it_tests': localflavor_it_tests,
'localflavor_jp_tests': localflavor_jp_tests,
+ 'localflavor_kw_tests': localflavor_kw_tests,
'localflavor_nl_tests': localflavor_nl_tests,
'localflavor_pl_tests': localflavor_pl_tests,
+ 'localflavor_pt_tests': localflavor_pt_tests,
'localflavor_ro_tests': localflavor_ro_tests,
'localflavor_se_tests': localflavor_se_tests,
'localflavor_sk_tests': localflavor_sk_tests,
'localflavor_uk_tests': localflavor_uk_tests,
'localflavor_us_tests': localflavor_us_tests,
+ 'localflavor_uy_tests': localflavor_uy_tests,
'localflavor_za_tests': localflavor_za_tests,
'regression_tests': regression_tests,
'formset_tests': formset_tests,
diff --git a/tests/regressiontests/forms/widgets.py b/tests/regressiontests/forms/widgets.py
index 84ae61f527..6ea9298d4c 100644
--- a/tests/regressiontests/forms/widgets.py
+++ b/tests/regressiontests/forms/widgets.py
@@ -10,6 +10,8 @@ tests = r"""
... from decimal import Decimal
... except ImportError:
... from django.utils._decimal import Decimal
+>>> from django.utils.translation import activate, deactivate
+>>> from django.conf import settings
###########
# Widgets #
@@ -1082,6 +1084,13 @@ True
False
>>> w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), [u'06/05/2008', u'12:41'])
True
+>>> activate('de-at')
+>>> settings.USE_L10N = True
+>>> w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), [u'06.05.2008', u'12:41'])
+True
+>>> deactivate()
+>>> settings.USE_L10N = False
+
# DateTimeInput ###############################################################
@@ -1099,6 +1108,12 @@ u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
u'<input type="text" name="date" value="2007-09-17 12:51:00" />'
+>>> activate('de-at')
+>>> settings.USE_L10N = True
+>>> w.render('date', d)
+u'<input type="text" name="date" value="17.09.2007 12:51:34" />'
+>>> deactivate()
+>>> settings.USE_L10N = False
Use 'format' to change the way a value is displayed.
>>> w = DateTimeInput(format='%d/%m/%Y %H:%M')
@@ -1107,6 +1122,7 @@ u'<input type="text" name="date" value="17/09/2007 12:51" />'
>>> w._has_changed(d, '17/09/2007 12:51')
False
+
# DateInput ###################################################################
>>> w = DateInput()
@@ -1125,6 +1141,13 @@ We should be able to initialize from a unicode value.
>>> w.render('date', u'2007-09-17')
u'<input type="text" name="date" value="2007-09-17" />'
+>>> activate('de-at')
+>>> settings.USE_L10N = True
+>>> w.render('date', d)
+u'<input type="text" name="date" value="17.09.2007" />'
+>>> deactivate()
+>>> settings.USE_L10N = False
+
Use 'format' to change the way a value is displayed.
>>> w = DateInput(format='%d/%m/%Y')
>>> w.render('date', d)
@@ -1153,6 +1176,13 @@ We should be able to initialize from a unicode value.
>>> w.render('time', u'13:12:11')
u'<input type="text" name="time" value="13:12:11" />'
+>>> activate('de-at')
+>>> settings.USE_L10N = True
+>>> w.render('date', d)
+u'<input type="text" name="date" value="17.09.2007" />'
+>>> deactivate()
+>>> settings.USE_L10N = False
+
Use 'format' to change the way a value is displayed.
>>> w = TimeInput(format='%H:%M')
>>> w.render('time', t)
@@ -1176,6 +1206,12 @@ u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" n
u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:34" />'
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:00" />'
+>>> activate('de-at')
+>>> settings.USE_L10N = True
+>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
+u'<input type="hidden" name="date_0" value="17.09.2007" /><input type="hidden" name="date_1" value="12:51:00" />'
+>>> deactivate()
+>>> settings.USE_L10N = False
"""
diff --git a/tests/regressiontests/i18n/forms.py b/tests/regressiontests/i18n/forms.py
index b8244f6ca7..8df066c6fd 100644
--- a/tests/regressiontests/i18n/forms.py
+++ b/tests/regressiontests/i18n/forms.py
@@ -1,5 +1,6 @@
from django import template, forms
from django.forms.extras import SelectDateWidget
+from models import Company
class I18nForm(forms.Form):
decimal_field = forms.DecimalField()
@@ -11,3 +12,6 @@ class I18nForm(forms.Form):
class SelectDateForm(forms.Form):
date_field = forms.DateField(widget=SelectDateWidget)
+class CompanyForm(forms.ModelForm):
+ class Meta:
+ model = Company
diff --git a/tests/regressiontests/i18n/models.py b/tests/regressiontests/i18n/models.py
index e1eefb8477..56f158578b 100644
--- a/tests/regressiontests/i18n/models.py
+++ b/tests/regressiontests/i18n/models.py
@@ -1,12 +1,16 @@
+from datetime import datetime
from django.db import models
from django.utils.translation import ugettext_lazy as _
class TestModel(models.Model):
text = models.CharField(max_length=10, default=_('Anything'))
+class Company(models.Model):
+ name = models.CharField(max_length=50)
+ date_added = models.DateTimeField(default=datetime(1799,1,31,23,59,59,0))
+
__test__ = {'API_TESTS': '''
>>> tm = TestModel()
>>> tm.save()
'''
}
-
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
index 42dfe91659..2df08a6648 100644
--- a/tests/regressiontests/i18n/tests.py
+++ b/tests/regressiontests/i18n/tests.py
@@ -1,15 +1,17 @@
+# -*- encoding: utf-8 -*-
import sys
import decimal
import datetime
from django.template import Template, Context
from django.conf import settings
-from django.utils.formats import get_format, date_format, number_format, localize
+from django.utils.formats import get_format, date_format, time_format, number_format, localize, localize_input
from django.utils.numberformat import format
from django.test import TestCase, client
-from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy
+from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy, to_locale
+
+from forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
-from forms import I18nForm, SelectDateForm, SelectDateWidget
class TranslationTests(TestCase):
@@ -80,6 +82,23 @@ class TranslationTests(TestCase):
finally:
deactivate()
+ def test_to_locale(self):
+ """
+ Tests the to_locale function and the special case of Serbian Latin
+ (refs #12230 and r11299)
+ """
+ self.assertEqual(to_locale('en-us'), 'en_US')
+ self.assertEqual(to_locale('sr-lat'), 'sr_Lat')
+
+ def test_to_language(self):
+ """
+ Test the to_language function
+ """
+ from django.utils.translation.trans_real import to_language
+ self.assertEqual(to_language('en_US'), 'en-us')
+ self.assertEqual(to_language('sr_Lat'), 'sr-lat')
+
+
class FormattingTests(TestCase):
def setUp(self):
@@ -90,8 +109,10 @@ class FormattingTests(TestCase):
self.f = 99999.999
self.d = datetime.date(2009, 12, 31)
self.dt = datetime.datetime(2009, 12, 31, 20, 50)
+ self.t = datetime.time(10, 15, 48)
self.ctxt = Context({
'n': self.n,
+ 't': self.t,
'd': self.d,
'dt': self.dt,
'f': self.f
@@ -102,10 +123,10 @@ class FormattingTests(TestCase):
settings.USE_I18N = self._use_i18n
settings.USE_L10N = self._use_l10n
settings.USE_THOUSAND_SEPARATOR = self._use_thousand_separator
-
+
def test_locale_independent(self):
"""
- Localization of dates and numbers
+ Localization of numbers
"""
settings.USE_L10N = True
settings.USE_THOUSAND_SEPARATOR = False
@@ -118,6 +139,10 @@ class FormattingTests(TestCase):
self.assertEqual(u'-66666.6', format(-66666.666, decimal_sep='.', decimal_pos=1))
self.assertEqual(u'-66666.0', format(int('-66666'), decimal_sep='.', decimal_pos=1))
+ # date filter
+ self.assertEqual(u'31.12.2009 в 20:50', Template('{{ dt|date:"d.m.Y в H:i" }}').render(self.ctxt))
+ self.assertEqual(u'⌚ 10:15', Template('{{ t|time:"⌚ H:i" }}').render(self.ctxt))
+
def test_l10n_disabled(self):
"""
Catalan locale with format i18n disabled translations will be used,
@@ -129,6 +154,7 @@ class FormattingTests(TestCase):
self.assertEqual('N j, Y', get_format('DATE_FORMAT'))
self.assertEqual(0, get_format('FIRST_DAY_OF_WEEK'))
self.assertEqual('.', get_format('DECIMAL_SEPARATOR'))
+ self.assertEqual(u'10:15 a.m.', time_format(self.t))
self.assertEqual(u'des. 31, 2009', date_format(self.d))
self.assertEqual(u'desembre 2009', date_format(self.d, 'YEAR_MONTH_FORMAT'))
self.assertEqual(u'12/31/2009 8:50 p.m.', date_format(self.dt, 'SHORT_DATETIME_FORMAT'))
@@ -143,6 +169,7 @@ class FormattingTests(TestCase):
self.assertEqual(u'2009-12-31 20:50:00', Template('{{ dt }}').render(self.ctxt))
self.assertEqual(u'66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
self.assertEqual(u'100000.0', Template('{{ f|floatformat }}').render(self.ctxt))
+ self.assertEqual(u'10:15 a.m.', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt))
self.assertEqual(u'12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
self.assertEqual(u'12/31/2009 8:50 p.m.', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt))
@@ -168,7 +195,7 @@ class FormattingTests(TestCase):
self.assertEqual(datetime.date(2009, 12, 31), form2.cleaned_data['date_field'])
self.assertEqual(
u'<select name="mydate_month" id="id_mydate_month">\n<option value="1">gener</option>\n<option value="2">febrer</option>\n<option value="3">mar\xe7</option>\n<option value="4">abril</option>\n<option value="5">maig</option>\n<option value="6">juny</option>\n<option value="7">juliol</option>\n<option value="8">agost</option>\n<option value="9">setembre</option>\n<option value="10">octubre</option>\n<option value="11">novembre</option>\n<option value="12" selected="selected">desembre</option>\n</select>\n<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>',
- SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
+ SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31))
)
finally:
deactivate()
@@ -183,6 +210,7 @@ class FormattingTests(TestCase):
self.assertEqual('j \de F \de Y', get_format('DATE_FORMAT'))
self.assertEqual(1, get_format('FIRST_DAY_OF_WEEK'))
self.assertEqual(',', get_format('DECIMAL_SEPARATOR'))
+ self.assertEqual(u'10:15:48', time_format(self.t))
self.assertEqual(u'31 de desembre de 2009', date_format(self.d))
self.assertEqual(u'desembre del 2009', date_format(self.d, 'YEAR_MONTH_FORMAT'))
self.assertEqual(u'31/12/2009 20:50', date_format(self.dt, 'SHORT_DATETIME_FORMAT'))
@@ -209,6 +237,7 @@ class FormattingTests(TestCase):
self.assertEqual(u'31 de desembre de 2009 a les 20:50', Template('{{ dt }}').render(self.ctxt))
self.assertEqual(u'66666,67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
self.assertEqual(u'100000,0', Template('{{ f|floatformat }}').render(self.ctxt))
+ self.assertEqual(u'10:15:48', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt))
self.assertEqual(u'31/12/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
self.assertEqual(u'31/12/2009 20:50', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt))
@@ -235,7 +264,7 @@ class FormattingTests(TestCase):
self.assertEqual(datetime.date(2009, 12, 31), form4.cleaned_data['date_field'])
self.assertEqual(
u'<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_month" id="id_mydate_month">\n<option value="1">gener</option>\n<option value="2">febrer</option>\n<option value="3">mar\xe7</option>\n<option value="4">abril</option>\n<option value="5">maig</option>\n<option value="6">juny</option>\n<option value="7">juliol</option>\n<option value="8">agost</option>\n<option value="9">setembre</option>\n<option value="10">octubre</option>\n<option value="11">novembre</option>\n<option value="12" selected="selected">desembre</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>',
- SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
+ SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31))
)
finally:
deactivate()
@@ -300,7 +329,7 @@ class FormattingTests(TestCase):
self.assertEqual(datetime.date(2009, 12, 31), form6.cleaned_data['date_field'])
self.assertEqual(
u'<select name="mydate_month" id="id_mydate_month">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12" selected="selected">December</option>\n</select>\n<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>',
- SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
+ SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31))
)
finally:
deactivate()
@@ -323,6 +352,28 @@ class FormattingTests(TestCase):
finally:
deactivate()
+ def test_localized_input(self):
+ """
+ Tests if form input is correctly localized
+ """
+ settings.USE_L10N = True
+ activate('de-at')
+ try:
+ form6 = CompanyForm({
+ 'name': u'acme',
+ 'date_added': datetime.datetime(2009, 12, 31, 6, 0, 0),
+ })
+ form6.save()
+ self.assertEqual(True, form6.is_valid())
+ self.assertEqual(
+ form6.as_ul(),
+ u'<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" value="acme" maxlength="50" /></li>\n<li><label for="id_date_added">Date added:</label> <input type="text" name="date_added" value="31.12.2009 06:00:00" id="id_date_added" /></li>'
+ )
+ self.assertEqual(localize_input(datetime.datetime(2009, 12, 31, 6, 0, 0)), '31.12.2009 06:00:00')
+ self.assertEqual(datetime.datetime(2009, 12, 31, 6, 0, 0), form6.cleaned_data['date_added'])
+ finally:
+ deactivate()
+
class MiscTests(TestCase):
def test_parse_spec_http_header(self):
diff --git a/tests/regressiontests/localflavor/forms.py b/tests/regressiontests/localflavor/forms.py
index 49635b02fb..2dd1da6dd0 100644
--- a/tests/regressiontests/localflavor/forms.py
+++ b/tests/regressiontests/localflavor/forms.py
@@ -5,10 +5,3 @@ class PlaceForm(ModelForm):
"""docstring for PlaceForm"""
class Meta:
model = Place
-from django.forms import ModelForm
-from models import Place
-
-class PlaceForm(ModelForm):
- """docstring for PlaceForm"""
- class Meta:
- model = Place
diff --git a/tests/regressiontests/localflavor/models.py b/tests/regressiontests/localflavor/models.py
index 079c7bd982..f74a5051d4 100644
--- a/tests/regressiontests/localflavor/models.py
+++ b/tests/regressiontests/localflavor/models.py
@@ -6,11 +6,3 @@ class Place(models.Model):
state_req = USStateField()
state_default = USStateField(default="CA", blank=True)
name = models.CharField(max_length=20)
-from django.db import models
-from django.contrib.localflavor.us.models import USStateField
-
-class Place(models.Model):
- state = USStateField(blank=True)
- state_req = USStateField()
- state_default = USStateField(default="CA", blank=True)
- name = models.CharField(max_length=20)
diff --git a/tests/regressiontests/localflavor/tests.py b/tests/regressiontests/localflavor/tests.py
index 61e0e5b5e6..0ea3c52568 100644
--- a/tests/regressiontests/localflavor/tests.py
+++ b/tests/regressiontests/localflavor/tests.py
@@ -81,86 +81,3 @@ class USLocalflavorTests(TestCase):
<option value="WY">Wyoming</option>
</select>"""
self.assertEqual(str(self.form['state']), state_select_html)
-from django.test import TestCase
-from models import Place
-from forms import PlaceForm
-
-class USLocalflavorTests(TestCase):
- def setUp(self):
- self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'})
-
- def test_get_display_methods(self):
- """Test that the get_*_display() methods are added to the model instances."""
- place = self.form.save()
- self.assertEqual(place.get_state_display(), 'Georgia')
- self.assertEqual(place.get_state_req_display(), 'North Carolina')
-
- def test_required(self):
- """Test that required USStateFields throw appropriate errors."""
- form = PlaceForm({'state':'GA', 'name':'Place in GA'})
- self.assertFalse(form.is_valid())
- self.assertEqual(form.errors['state_req'], [u'This field is required.'])
-
- def test_field_blank_option(self):
- """Test that the empty option is there."""
- state_select_html = """\
-<select name="state" id="id_state">
-<option value="">---------</option>
-<option value="AL">Alabama</option>
-<option value="AK">Alaska</option>
-<option value="AS">American Samoa</option>
-<option value="AZ">Arizona</option>
-<option value="AR">Arkansas</option>
-<option value="CA">California</option>
-<option value="CO">Colorado</option>
-<option value="CT">Connecticut</option>
-<option value="DE">Delaware</option>
-<option value="DC">District of Columbia</option>
-<option value="FL">Florida</option>
-<option value="GA" selected="selected">Georgia</option>
-<option value="GU">Guam</option>
-<option value="HI">Hawaii</option>
-<option value="ID">Idaho</option>
-<option value="IL">Illinois</option>
-<option value="IN">Indiana</option>
-<option value="IA">Iowa</option>
-<option value="KS">Kansas</option>
-<option value="KY">Kentucky</option>
-<option value="LA">Louisiana</option>
-<option value="ME">Maine</option>
-<option value="MD">Maryland</option>
-<option value="MA">Massachusetts</option>
-<option value="MI">Michigan</option>
-<option value="MN">Minnesota</option>
-<option value="MS">Mississippi</option>
-<option value="MO">Missouri</option>
-<option value="MT">Montana</option>
-<option value="NE">Nebraska</option>
-<option value="NV">Nevada</option>
-<option value="NH">New Hampshire</option>
-<option value="NJ">New Jersey</option>
-<option value="NM">New Mexico</option>
-<option value="NY">New York</option>
-<option value="NC">North Carolina</option>
-<option value="ND">North Dakota</option>
-<option value="MP">Northern Mariana Islands</option>
-<option value="OH">Ohio</option>
-<option value="OK">Oklahoma</option>
-<option value="OR">Oregon</option>
-<option value="PA">Pennsylvania</option>
-<option value="PR">Puerto Rico</option>
-<option value="RI">Rhode Island</option>
-<option value="SC">South Carolina</option>
-<option value="SD">South Dakota</option>
-<option value="TN">Tennessee</option>
-<option value="TX">Texas</option>
-<option value="UT">Utah</option>
-<option value="VT">Vermont</option>
-<option value="VI">Virgin Islands</option>
-<option value="VA">Virginia</option>
-<option value="WA">Washington</option>
-<option value="WV">West Virginia</option>
-<option value="WI">Wisconsin</option>
-<option value="WY">Wyoming</option>
-</select>"""
- self.assertEqual(str(self.form['state']), state_select_html)
diff --git a/tests/regressiontests/settings/__init__.py b/tests/regressiontests/settings/__init__.py
new file mode 100644
index 0000000000..76e845e16b
--- /dev/null
+++ b/tests/regressiontests/settings/__init__.py
@@ -0,0 +1,4 @@
+# Settings file automatically generated by regressiontests.admin_scripts test case
+DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'SUPPORTS_TRANSACTIONS': True, 'NAME': ':memory:', 'TEST_CHARSET': None, 'TIME_ZONE': 'America/Chicago', 'TEST_COLLATION': None, 'PORT': '', 'HOST': '', 'USER': '', 'TEST_NAME': None, 'PASSWORD': '', 'OPTIONS': {}}, 'other': {'ENGINE': 'django.db.backends.sqlite3', 'SUPPORTS_TRANSACTIONS': True, 'NAME': 'other_db14006', 'TEST_CHARSET': None, 'TIME_ZONE': 'America/Chicago', 'TEST_COLLATION': None, 'PORT': '', 'HOST': '', 'USER': '', 'TEST_NAME': 'other_db14006', 'PASSWORD': '', 'OPTIONS': {}}}
+ROOT_URLCONF = 'urls'
+INSTALLED_APPS = ['django.contrib.auth', 'django.contrib.contenttypes', 'admin_scripts']
diff --git a/tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.mo b/tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.mo
new file mode 100644
index 0000000000..148cba533b
--- /dev/null
+++ b/tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.mo
Binary files differ
diff --git a/tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.po b/tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.po
new file mode 100644
index 0000000000..a0ff0152ed
--- /dev/null
+++ b/tests/regressiontests/views/locale/ru/LC_MESSAGES/djangojs.po
@@ -0,0 +1,20 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-09-15 16:45+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "this is to be translated"
+msgstr "перевод" \ No newline at end of file
diff --git a/tests/regressiontests/views/tests/i18n.py b/tests/regressiontests/views/tests/i18n.py
index ebe97ab2dc..b0fa3e3f47 100644
--- a/tests/regressiontests/views/tests/i18n.py
+++ b/tests/regressiontests/views/tests/i18n.py
@@ -4,6 +4,7 @@ import gettext
from django.conf import settings
from django.test import TestCase
from django.utils.translation import activate
+from django.utils.text import javascript_quote
from regressiontests.views.urls import locale_dir
@@ -20,11 +21,12 @@ class I18NTests(TestCase):
def test_jsi18n(self):
"""The javascript_catalog can be deployed with language settings"""
- for lang_code in ['es', 'fr', 'en']:
+ for lang_code in ['es', 'fr', 'en', 'ru']:
activate(lang_code)
catalog = gettext.translation('djangojs', locale_dir, [lang_code])
trans_txt = catalog.ugettext('this is to be translated')
response = self.client.get('/views/jsi18n/')
# in response content must to be a line like that:
# catalog['this is to be translated'] = 'same_that_trans_txt'
- self.assertContains(response, trans_txt, 1)
+ # javascript_quote is used to be able to check unicode strings
+ self.assertContains(response, javascript_quote(trans_txt), 1)
diff --git a/tests/runtests.py b/tests/runtests.py
index 1cd95b9fef..f2ea5f1271 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -162,7 +162,7 @@ def django_tests(verbosity, interactive, failfast, test_labels):
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, failfast=failfast,
extra_tests=extra_tests)
if failures:
- sys.exit(failures)
+ sys.exit(bool(failures))
# Restore the old settings.
settings.INSTALLED_APPS = old_installed_apps