From a97abcffc27c5026041afccb1d76f7e4e4b1df69 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Wed, 28 Nov 2007 21:51:17 +0000 Subject: [PATCH] queryset-refactor: Merged from trunk up to [6724]. git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6726 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/bin/compile-messages.py | 5 +- django/conf/global_settings.py | 2 + django/conf/locale/pl/LC_MESSAGES/django.mo | Bin 49121 -> 50899 bytes django/conf/locale/pl/LC_MESSAGES/django.po | 300 +++++++-------- django/contrib/admin/media/css/base.css | 4 +- .../media/js/admin/RelatedObjectLookups.js | 14 + .../contrib/admin/templatetags/admin_list.py | 4 +- .../admin/templatetags/admin_modify.py | 2 +- django/contrib/admin/views/main.py | 7 +- django/core/cache/backends/locmem.py | 7 +- django/core/management/__init__.py | 49 +-- django/core/paginator.py | 2 +- django/middleware/gzip.py | 18 +- django/newforms/extras/widgets.py | 3 +- django/newforms/models.py | 37 +- django/newforms/widgets.py | 25 +- django/template/__init__.py | 4 +- django/template/defaultfilters.py | 3 + django/test/testcases.py | 6 +- django/utils/cache.py | 23 +- django/utils/datastructures.py | 71 ++-- django/utils/translation/trans_real.py | 7 +- django/views/debug.py | 12 +- docs/i18n.txt | 2 +- docs/settings.txt | 2 +- docs/templates_python.txt | 135 ++++--- tests/modeltests/pagination/models.py | 3 +- tests/regressiontests/cache/tests.py | 30 +- tests/regressiontests/datastructures/tests.py | 12 + tests/regressiontests/forms/error_messages.py | 360 ++++++++++++++++++ tests/regressiontests/forms/models.py | 4 + tests/regressiontests/forms/widgets.py | 54 +++ tests/regressiontests/templates/filters.py | 6 + tests/regressiontests/templates/tests.py | 6 + 34 files changed, 892 insertions(+), 327 deletions(-) create mode 100644 tests/regressiontests/forms/error_messages.py diff --git a/django/bin/compile-messages.py b/django/bin/compile-messages.py index 8693022644..0d71413e5a 100755 --- a/django/bin/compile-messages.py +++ b/django/bin/compile-messages.py @@ -11,11 +11,10 @@ except NameError: def compile_messages(locale=None): - basedirs = [os.path.join('conf', 'locale'), 'locale'] + basedirs = (os.path.join('conf', 'locale'), 'locale') if os.environ.get('DJANGO_SETTINGS_MODULE'): from django.conf import settings - if hasattr(settings, 'LOCALE_PATHS'): - basedirs += settings.LOCALE_PATHS + basedirs += settings.LOCALE_PATHS # Gather existing directories. basedirs = set(map(os.path.abspath, filter(os.path.isdir, basedirs))) diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 994e908caf..2853d6c618 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -90,6 +90,8 @@ LANGUAGES_BIDI = ("he", "ar", "fa") # to load the internationalization machinery. USE_I18N = True +LOCALE_PATHS = () + # Not-necessarily-technical managers of the site. They get broken link # notifications and other various e-mails. MANAGERS = ADMINS diff --git a/django/conf/locale/pl/LC_MESSAGES/django.mo b/django/conf/locale/pl/LC_MESSAGES/django.mo index 391647fbb0a930b3aa8d2efc58f946f773969d5b..b5ed35225d245408eca4adde1e14c5cf08106495 100644 GIT binary patch delta 14344 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 jcok%A8C0^wz>$?k^dCBWMDF0BdAs+uC\n" "Language-Team: Polish \n" @@ -196,7 +196,7 @@ msgstr "Uproszczony chiński" msgid "Traditional Chinese" msgstr "Chiński tradycyjny" -#: contrib/admin/filterspecs.py:42 +#: contrib/admin/filterspecs.py:44 #, python-format msgid "" "

By %s:

\n" @@ -205,71 +205,71 @@ msgstr "" "

Przez %s:

\n" "\n" -#: contrib/admin/filterspecs.py:72 contrib/admin/filterspecs.py:90 -#: contrib/admin/filterspecs.py:145 contrib/admin/filterspecs.py:171 +#: contrib/admin/filterspecs.py:74 contrib/admin/filterspecs.py:92 +#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173 msgid "All" msgstr "Wszystko" -#: contrib/admin/filterspecs.py:111 +#: contrib/admin/filterspecs.py:113 msgid "Any date" msgstr "Dowolna data" -#: contrib/admin/filterspecs.py:112 +#: contrib/admin/filterspecs.py:114 msgid "Today" msgstr "Dzisiaj" -#: contrib/admin/filterspecs.py:115 +#: contrib/admin/filterspecs.py:117 msgid "Past 7 days" msgstr "Ostatnie 7 dni" -#: contrib/admin/filterspecs.py:117 +#: contrib/admin/filterspecs.py:119 msgid "This month" msgstr "Ten miesiąc" -#: contrib/admin/filterspecs.py:119 +#: contrib/admin/filterspecs.py:121 msgid "This year" msgstr "Ten rok" -#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221 -#: oldforms/__init__.py:591 +#: contrib/admin/filterspecs.py:147 newforms/widgets.py:229 +#: oldforms/__init__.py:592 msgid "Yes" msgstr "Tak" -#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221 -#: oldforms/__init__.py:591 +#: contrib/admin/filterspecs.py:147 newforms/widgets.py:229 +#: oldforms/__init__.py:592 msgid "No" msgstr "Nie" -#: contrib/admin/filterspecs.py:152 newforms/widgets.py:221 -#: oldforms/__init__.py:591 +#: contrib/admin/filterspecs.py:154 newforms/widgets.py:229 +#: oldforms/__init__.py:592 msgid "Unknown" msgstr "Nieznany" -#: contrib/admin/models.py:17 +#: contrib/admin/models.py:18 msgid "action time" msgstr "czas akcji" -#: contrib/admin/models.py:20 +#: contrib/admin/models.py:21 msgid "object id" msgstr "id obiektu" -#: contrib/admin/models.py:21 +#: contrib/admin/models.py:22 msgid "object repr" msgstr "reprezentacj obiektu" -#: contrib/admin/models.py:22 +#: contrib/admin/models.py:23 msgid "action flag" msgstr "flaga akcji" -#: contrib/admin/models.py:23 +#: contrib/admin/models.py:24 msgid "change message" msgstr "zmień wiadomość" -#: contrib/admin/models.py:26 +#: contrib/admin/models.py:27 msgid "log entry" msgstr "log" -#: contrib/admin/models.py:27 +#: contrib/admin/models.py:28 msgid "log entries" msgstr "logi" @@ -472,7 +472,7 @@ msgid "Password:" msgstr "Hasło:" #: contrib/admin/templates/admin/login.html:25 -#: contrib/admin/views/decorators.py:24 +#: contrib/admin/views/decorators.py:25 msgid "Log in" msgstr "Zaloguj się" @@ -764,17 +764,17 @@ msgstr "Teraz:" msgid "Change:" msgstr "Zmień:" -#: contrib/admin/templatetags/admin_list.py:254 +#: contrib/admin/templatetags/admin_list.py:255 msgid "All dates" msgstr "Wszystkie daty" -#: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:264 +#: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:267 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully." msgstr "%(name)s \"%(obj)s\" dodany pomyślnie." -#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:268 -#: contrib/admin/views/main.py:354 +#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:271 +#: contrib/admin/views/main.py:357 msgid "You may edit it again below." msgstr "Możesz ponownie edytować wpis poniżej." @@ -791,7 +791,7 @@ msgstr "Hasło zostało zmienione pomyślnie." msgid "Change password: %s" msgstr "Zmień hasło: %s" -#: contrib/admin/views/decorators.py:10 contrib/auth/forms.py:60 +#: contrib/admin/views/decorators.py:11 contrib/auth/forms.py:60 msgid "" "Please enter a correct username and password. Note that both fields are case-" "sensitive." @@ -799,7 +799,7 @@ msgstr "" "Proszę wpisać poprawną nazwę użytkownika i hasło. Uwaga: wielkość liter ma " "znaczenie." -#: contrib/admin/views/decorators.py:62 +#: contrib/admin/views/decorators.py:63 msgid "" "Please log in again, because your session has expired. Don't worry: Your " "submission has been saved." @@ -807,7 +807,7 @@ msgstr "" "Zaloguj się ponownie. Twoja sesja wygasła lecz twoje zgłoszenie zostało " "zapisane." -#: contrib/admin/views/decorators.py:69 +#: contrib/admin/views/decorators.py:70 msgid "" "Looks like your browser isn't configured to accept cookies. Please enable " "cookies, reload this page, and try again." @@ -815,246 +815,246 @@ msgstr "" "Twoja przeglądarka nie chce akceptować ciasteczek. Zmień jej ustawienia i " "spróbuj ponownie." -#: contrib/admin/views/decorators.py:83 +#: contrib/admin/views/decorators.py:84 msgid "Usernames cannot contain the '@' character." msgstr "Nazwy użytkowników nie mogą zawierać znaków '@'." -#: contrib/admin/views/decorators.py:85 +#: contrib/admin/views/decorators.py:86 #, python-format msgid "Your e-mail address is not your username. Try '%s' instead." msgstr "Twój adres e-mail to nie jest twój login. Spróbuj '%s'." -#: contrib/admin/views/doc.py:47 contrib/admin/views/doc.py:49 -#: contrib/admin/views/doc.py:51 +#: contrib/admin/views/doc.py:48 contrib/admin/views/doc.py:50 +#: contrib/admin/views/doc.py:52 msgid "tag:" msgstr "tag:" -#: contrib/admin/views/doc.py:78 contrib/admin/views/doc.py:80 -#: contrib/admin/views/doc.py:82 +#: contrib/admin/views/doc.py:79 contrib/admin/views/doc.py:81 +#: contrib/admin/views/doc.py:83 msgid "filter:" msgstr "filtr:" -#: contrib/admin/views/doc.py:136 contrib/admin/views/doc.py:138 -#: contrib/admin/views/doc.py:140 +#: contrib/admin/views/doc.py:137 contrib/admin/views/doc.py:139 +#: contrib/admin/views/doc.py:141 msgid "view:" msgstr "widok:" -#: contrib/admin/views/doc.py:165 +#: contrib/admin/views/doc.py:166 #, python-format msgid "App %r not found" msgstr "Aplikacja %r nie została znaleziona" -#: contrib/admin/views/doc.py:172 +#: contrib/admin/views/doc.py:173 #, python-format msgid "Model %(name)r not found in app %(label)r" msgstr "Model %(name)r nie został znaleziony w aplikacji %(label)r" -#: contrib/admin/views/doc.py:184 +#: contrib/admin/views/doc.py:185 #, python-format msgid "the related `%(label)s.%(type)s` object" msgstr "powiązany obiekt `%(label)s.%(type)s`" -#: contrib/admin/views/doc.py:184 contrib/admin/views/doc.py:206 -#: contrib/admin/views/doc.py:220 contrib/admin/views/doc.py:225 +#: contrib/admin/views/doc.py:185 contrib/admin/views/doc.py:207 +#: contrib/admin/views/doc.py:221 contrib/admin/views/doc.py:226 msgid "model:" msgstr "model:" -#: contrib/admin/views/doc.py:215 +#: contrib/admin/views/doc.py:216 #, python-format msgid "related `%(label)s.%(name)s` objects" msgstr "powiązane obiekty `%(label)s.%(name)s`" -#: contrib/admin/views/doc.py:220 +#: contrib/admin/views/doc.py:221 #, python-format msgid "all %s" msgstr "wszystkie %s" -#: contrib/admin/views/doc.py:225 +#: contrib/admin/views/doc.py:226 #, python-format msgid "number of %s" msgstr "liczba %s" -#: contrib/admin/views/doc.py:230 +#: contrib/admin/views/doc.py:231 #, python-format msgid "Fields on %s objects" msgstr "Pola obiektów %s" -#: contrib/admin/views/doc.py:292 contrib/admin/views/doc.py:303 -#: contrib/admin/views/doc.py:305 contrib/admin/views/doc.py:311 -#: contrib/admin/views/doc.py:312 contrib/admin/views/doc.py:314 +#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:304 +#: contrib/admin/views/doc.py:306 contrib/admin/views/doc.py:312 +#: contrib/admin/views/doc.py:313 contrib/admin/views/doc.py:315 msgid "Integer" msgstr "Liczba całkowita" -#: contrib/admin/views/doc.py:293 +#: contrib/admin/views/doc.py:294 msgid "Boolean (Either True or False)" msgstr "Wartość logiczna (True, False - prawda lub fałsz)" -#: contrib/admin/views/doc.py:294 contrib/admin/views/doc.py:313 +#: contrib/admin/views/doc.py:295 contrib/admin/views/doc.py:314 #, python-format msgid "String (up to %(max_length)s)" msgstr "Łańcuch (do %(max_length)s znaków)" -#: contrib/admin/views/doc.py:295 +#: contrib/admin/views/doc.py:296 msgid "Comma-separated integers" msgstr "Liczby całkowite rozdzielone przecinkami" -#: contrib/admin/views/doc.py:296 +#: contrib/admin/views/doc.py:297 msgid "Date (without time)" msgstr "Data (bez godziny)" -#: contrib/admin/views/doc.py:297 +#: contrib/admin/views/doc.py:298 msgid "Date (with time)" msgstr "Data (z godziną)" -#: contrib/admin/views/doc.py:298 +#: contrib/admin/views/doc.py:299 msgid "Decimal number" msgstr "Numer dziesiętny" -#: contrib/admin/views/doc.py:299 +#: contrib/admin/views/doc.py:300 msgid "E-mail address" msgstr "Adres e-mail" -#: contrib/admin/views/doc.py:300 contrib/admin/views/doc.py:301 -#: contrib/admin/views/doc.py:304 +#: contrib/admin/views/doc.py:301 contrib/admin/views/doc.py:302 +#: contrib/admin/views/doc.py:305 msgid "File path" msgstr "Ścieżka do pliku" -#: contrib/admin/views/doc.py:302 +#: contrib/admin/views/doc.py:303 msgid "Floating point number" msgstr "Liczba zmiennoprzecinkowa" -#: contrib/admin/views/doc.py:306 contrib/comments/models.py:85 +#: contrib/admin/views/doc.py:307 contrib/comments/models.py:85 msgid "IP address" msgstr "Adres IP" -#: contrib/admin/views/doc.py:308 +#: contrib/admin/views/doc.py:309 msgid "Boolean (Either True, False or None)" msgstr "Wartość logiczna (True, False, None - prawda, fałsz lub nic)" -#: contrib/admin/views/doc.py:309 +#: contrib/admin/views/doc.py:310 msgid "Relation to parent model" msgstr "Relacja do modelu rodzica" -#: contrib/admin/views/doc.py:310 +#: contrib/admin/views/doc.py:311 msgid "Phone number" msgstr "Numer telefonu" -#: contrib/admin/views/doc.py:315 +#: contrib/admin/views/doc.py:316 msgid "Text" msgstr "Tekst" -#: contrib/admin/views/doc.py:316 +#: contrib/admin/views/doc.py:317 msgid "Time" msgstr "Czas" -#: contrib/admin/views/doc.py:317 contrib/flatpages/models.py:7 +#: contrib/admin/views/doc.py:318 contrib/flatpages/models.py:7 msgid "URL" msgstr "URL" -#: contrib/admin/views/doc.py:318 +#: contrib/admin/views/doc.py:319 msgid "U.S. state (two uppercase letters)" msgstr "Stan USA (dwie duże litery)" -#: contrib/admin/views/doc.py:319 +#: contrib/admin/views/doc.py:320 msgid "XML text" msgstr "Tekst XML" -#: contrib/admin/views/doc.py:345 +#: contrib/admin/views/doc.py:346 #, python-format msgid "%s does not appear to be a urlpattern object" msgstr "%s nie jest obiektem urlpattern" -#: contrib/admin/views/main.py:230 +#: contrib/admin/views/main.py:233 msgid "Site administration" msgstr "Administracja stroną" -#: contrib/admin/views/main.py:278 contrib/admin/views/main.py:363 +#: contrib/admin/views/main.py:281 contrib/admin/views/main.py:366 #, python-format msgid "You may add another %s below." msgstr "Możesz dodać nowy wpis %s poniżej." -#: contrib/admin/views/main.py:296 +#: contrib/admin/views/main.py:299 #, python-format msgid "Add %s" msgstr "Dodaj %s" -#: contrib/admin/views/main.py:342 +#: contrib/admin/views/main.py:345 #, python-format msgid "Added %s." msgstr "Dodano %s" -#: contrib/admin/views/main.py:342 contrib/admin/views/main.py:344 -#: contrib/admin/views/main.py:346 core/validators.py:283 +#: contrib/admin/views/main.py:345 contrib/admin/views/main.py:347 +#: contrib/admin/views/main.py:349 core/validators.py:283 #: db/models/manipulators.py:309 msgid "and" msgstr "i" -#: contrib/admin/views/main.py:344 +#: contrib/admin/views/main.py:347 #, python-format msgid "Changed %s." msgstr "Zmieniono %s" -#: contrib/admin/views/main.py:346 +#: contrib/admin/views/main.py:349 #, python-format msgid "Deleted %s." msgstr "Skasowano %s" -#: contrib/admin/views/main.py:349 +#: contrib/admin/views/main.py:352 msgid "No fields changed." msgstr "Żadne pole nie zmienione." -#: contrib/admin/views/main.py:352 +#: contrib/admin/views/main.py:355 #, python-format msgid "The %(name)s \"%(obj)s\" was changed successfully." msgstr "%(name)s \"%(obj)s\" zostało pomyślnie zmienione." -#: contrib/admin/views/main.py:360 +#: contrib/admin/views/main.py:363 #, 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/views/main.py:398 +#: contrib/admin/views/main.py:401 #, python-format msgid "Change %s" msgstr "Zmień %s" -#: contrib/admin/views/main.py:483 +#: contrib/admin/views/main.py:488 #, python-format msgid "One or more %(fieldname)s in %(name)s: %(obj)s" msgstr "Jedno lub więcej %(fieldname)s w %(name)s: %(obj)s" -#: contrib/admin/views/main.py:488 +#: contrib/admin/views/main.py:493 #, python-format msgid "One or more %(fieldname)s in %(name)s:" msgstr "Jedno lub więcej %(fieldname)s w %(name)s:" -#: contrib/admin/views/main.py:520 +#: contrib/admin/views/main.py:525 #, python-format msgid "The %(name)s \"%(obj)s\" was deleted successfully." msgstr "%(name)s \"%(obj)s\" usunięty pomyślnie." -#: contrib/admin/views/main.py:523 +#: contrib/admin/views/main.py:528 msgid "Are you sure?" msgstr "Jesteś pewien?" -#: contrib/admin/views/main.py:545 +#: contrib/admin/views/main.py:550 #, python-format msgid "Change history: %s" msgstr "Historia zmian: %s" -#: contrib/admin/views/main.py:579 +#: contrib/admin/views/main.py:584 #, python-format msgid "Select %s" msgstr "Zaznacz %s" -#: contrib/admin/views/main.py:579 +#: contrib/admin/views/main.py:584 #, python-format msgid "Select %s to change" msgstr "Zaznacz %s aby zmienić" -#: contrib/admin/views/main.py:780 +#: contrib/admin/views/main.py:785 msgid "Database error" msgstr "Błąd bazy danych" @@ -1618,7 +1618,7 @@ msgstr "-gi" msgid "rd" msgstr "-ci" -#: contrib/humanize/templatetags/humanize.py:50 +#: contrib/humanize/templatetags/humanize.py:52 #, python-format msgid "%(value).1f million" msgid_plural "%(value).1f million" @@ -1626,7 +1626,7 @@ msgstr[0] "%(value).1f milion" msgstr[1] "%(value).1f miliony" msgstr[2] "%(value).1f milionów" -#: contrib/humanize/templatetags/humanize.py:53 +#: contrib/humanize/templatetags/humanize.py:55 #, python-format msgid "%(value).1f billion" msgid_plural "%(value).1f billion" @@ -1634,7 +1634,7 @@ msgstr[0] "%(value).1f miliard" msgstr[1] "%(value).1f miliardy" msgstr[2] "%(value).1f miliardów" -#: contrib/humanize/templatetags/humanize.py:56 +#: contrib/humanize/templatetags/humanize.py:58 #, python-format msgid "%(value).1f trillion" msgid_plural "%(value).1f trillion" @@ -1642,51 +1642,51 @@ msgstr[0] "%(value).1f bilion" msgstr[1] "%(value).1f biliony" msgstr[2] "%(value).1f bilionów" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "one" msgstr "jeden" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "two" msgstr "dwa" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "three" msgstr "trzy" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "four" msgstr "cztery" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "five" msgstr "pięć" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "six" msgstr "sześć" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "seven" msgstr "siedem" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "eight" msgstr "osiem" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "nine" msgstr "dziewięć" -#: contrib/humanize/templatetags/humanize.py:90 +#: contrib/humanize/templatetags/humanize.py:94 msgid "today" msgstr "dzisiaj" -#: contrib/humanize/templatetags/humanize.py:92 +#: contrib/humanize/templatetags/humanize.py:96 msgid "tomorrow" msgstr "jutro" -#: contrib/humanize/templatetags/humanize.py:94 +#: contrib/humanize/templatetags/humanize.py:98 msgid "yesterday" msgstr "wczoraj" @@ -1705,8 +1705,7 @@ msgstr "To pole musi zawierać 7 lub 8 cyfr." #: contrib/localflavor/ar/forms.py:75 msgid "Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format." -msgstr "" -"Podaj poprawny numer CUIT w formacie XX-XXXXXXXX-X lub XXXXXXXXXXXX." +msgstr "Podaj poprawny numer CUIT w formacie XX-XXXXXXXX-X lub XXXXXXXXXXXX." #: contrib/localflavor/ar/forms.py:88 msgid "Invalid CUIT." @@ -1725,11 +1724,12 @@ msgid "Phone numbers must be in XX-XXXX-XXXX format." msgstr "Numery telefoniczne muszą być w formacie XX-XXXX-XXXX." #: contrib/localflavor/br/forms.py:68 -#, fuzzy msgid "" "Select a valid brazilian state. That state is not one of the available " "states." -msgstr "Wybierz poprawną wartość. Podana nie jest jednym z dostępnych wyborów." +msgstr "" +"Wybierz poprawny brazylijski stan. Ten stan nie jest jednym z dostępnych " +"stanów." #: contrib/localflavor/br/forms.py:105 msgid "This field requires at most 11 digits or 14 characters." @@ -1748,14 +1748,13 @@ msgid "Invalid CNPJ number." msgstr "Błędny numer CNPJ." #: contrib/localflavor/ca/forms.py:19 -#, fuzzy msgid "Enter a postal code in the format XXX XXX." -msgstr "Wpisz kod pocztowy w formacie XXXXX." +msgstr "Wpisz kod pocztowy w formacie XXX XXX." #: contrib/localflavor/ca/forms.py:81 -#, fuzzy msgid "Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format." -msgstr "Wpisz poprawny numer U.S. Social Security w formacie XXX-XX-XXXX." +msgstr "" +"Wpisz poprawny numer kanadyjskiego ubezpieczenia w formacie XXX-XXX-XXXX." #: contrib/localflavor/ch/ch_states.py:5 msgid "Aargau" @@ -1870,7 +1869,7 @@ msgid "" "Enter a valid Swiss identity or passport card number in X1234567<0 or " "1234567890 format." msgstr "" -"Podaj poprawny numer szwajarskiego dowodu osobistego lub paszportu w " +"Podaj poprawny numer szwajcarskiego dowodu osobistego lub paszportu w " "formacie X1234567<0 lub 1234567890." #: contrib/localflavor/cl/forms.py:32 @@ -2237,16 +2236,15 @@ msgid "Valencian Community" msgstr "" #: contrib/localflavor/es/forms.py:22 -#, fuzzy msgid "Enter a valid postal code in the range and format 01XXX - 52XXX." -msgstr "Wpisz kod pocztowy w formacie XXXXXXX lub XXX-XXXX." +msgstr "Wpisz kod pocztowy w zakresie i formacie 01XXX - 52XX." #: contrib/localflavor/es/forms.py:39 -#, fuzzy msgid "" "Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or " "9XXXXXXXX." -msgstr "Wpisz kod pocztowy w formacie XXXXXXX lub XXX-XXXX." +msgstr "" +"Wpisz numer telefoniczny w formacie 6XXXXXXXX, 8XXXXXXXX lub 9XXXXXXXX." #: contrib/localflavor/es/forms.py:73 contrib/localflavor/es/forms.py:108 #: db/models/fields/related.py:55 @@ -2256,36 +2254,33 @@ msgstr "Proszę wpisać poprawne %s." #: contrib/localflavor/es/forms.py:91 msgid "Invalid checksum for NIF." -msgstr "" +msgstr "Niepoprawna suma kontrolna NIF." #: contrib/localflavor/es/forms.py:97 msgid "Invalid checksum for NIE." -msgstr "" +msgstr "Niepoprawna suma kontrolna NIE." #: contrib/localflavor/es/forms.py:106 msgid "Invalid checksum for CIF." -msgstr "" +msgstr "Niepoprawna suma kontrolna CIF." #: contrib/localflavor/es/forms.py:136 -#, fuzzy msgid "" "Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX." msgstr "" -"Podaj poprawny niemiecki numer dowodu osobistego w formacie XXXXXXXXXXX-" -"XXXXXXX-XXXXXXX-X." +"Podaj poprawny numer konta bankowego w formacie XXXX-XXXX-XX-XXXXXXXXXX." #: contrib/localflavor/es/forms.py:150 msgid "Invalid checksum for bank account number." -msgstr "" +msgstr "Niepoprawna suma kontrolna numeru konta bankowego." #: contrib/localflavor/fi/forms.py:40 contrib/localflavor/fi/forms.py:45 msgid "Enter a valid Finnish social security number." msgstr "Wpis poprawny numer fińskiego ubezpieczenia socjalnego." #: contrib/localflavor/in_/forms.py:16 -#, fuzzy msgid "Enter a zip code in the format XXXXXXX." -msgstr "Wpisz kod pocztowy w formacie XXXXX-XXX." +msgstr "Wpisz kod pocztowy w formacie XXXXXXX." #: contrib/localflavor/is_/forms.py:17 msgid "" @@ -2502,19 +2497,16 @@ msgid "Okinawa" msgstr "" #: contrib/localflavor/nl/forms.py:25 -#, fuzzy msgid "Enter a valid postal code" msgstr "Wpisz poprawny kod pocztowy." #: contrib/localflavor/nl/forms.py:53 -#, fuzzy msgid "Enter a valid phone number" -msgstr "Wpisz poprawny numer VAT." +msgstr "Wpisz poprawny numer telefonu." #: contrib/localflavor/nl/forms.py:76 -#, fuzzy msgid "Enter a valid SoFi number" -msgstr "Wpisz poprawny numer VAT." +msgstr "Wpisz poprawny numer SoFi." #: contrib/localflavor/nl/nl_provinces.py:4 #, fuzzy @@ -2574,13 +2566,12 @@ msgid "Enter a valid Norwegian social security number." msgstr "Wpis poprawny numer norweskiego ubezpieczenia socjalnego." #: contrib/localflavor/pe/forms.py:36 -#, fuzzy msgid "This field requires 8 digits." -msgstr "To pole musi zawierać co najmniej 14 cyfr." +msgstr "To pole musi zawierać 8 cyfr." #: contrib/localflavor/pe/forms.py:59 msgid "This field requires 11 digits." -msgstr "To pole musi zawierać co najmniej 11 cyfr." +msgstr "To pole musi zawierać 11 cyfr." #: contrib/localflavor/pl/forms.py:41 msgid "National Identification Number consists of 11 digits." @@ -2676,9 +2667,8 @@ msgid "West Pomerania" msgstr "Zachodniopomorskie" #: contrib/localflavor/sk/forms.py:32 -#, fuzzy msgid "Enter a postal code in the format XXXXX or XXX XX." -msgstr "Wpisz kod pocztowy w formacie XXXXXXX lub XXX-XXXX." +msgstr "Wpisz kod pocztowy w formacie XXXXX or XXX XX." #: contrib/localflavor/sk/sk_districts.py:8 msgid "Banska Bystrica" @@ -3198,7 +3188,7 @@ msgid "Enter a valid e-mail address." msgstr "Wprowadź poprawny adres e-mail." #: core/validators.py:182 core/validators.py:474 newforms/fields.py:438 -#: oldforms/__init__.py:686 +#: oldforms/__init__.py:687 msgid "No file was submitted. Check the encoding type on the form." msgstr "Nie wysłano żadnego pliku. Sprawdź typ kodowania formularza." @@ -3460,7 +3450,7 @@ msgstr "Już istnieje %(optname)s z %(fieldname)s." #: db/models/fields/__init__.py:161 db/models/fields/__init__.py:318 #: db/models/fields/__init__.py:735 db/models/fields/__init__.py:746 -#: newforms/fields.py:45 newforms/models.py:220 oldforms/__init__.py:373 +#: newforms/fields.py:45 newforms/models.py:220 oldforms/__init__.py:374 msgid "This field is required." msgstr "To pole jest wymagane." @@ -3523,15 +3513,15 @@ msgstr "Wpisz poprawną wartość." #, python-format msgid "Ensure this value has at most %(max)d characters (it has %(length)d)." msgstr "" -"Upewnij się, że ta wartość ma co najwyżej %(max)d znaków " -"(ma długość %(length)d)." +"Upewnij się, że ta wartość ma co najwyżej %(max)d znaków (ma długość %" +"(length)d)." #: newforms/fields.py:130 #, python-format msgid "Ensure this value has at least %(min)d characters (it has %(length)d)." msgstr "" -"Upewnij się, że ta wartość ma co najmniej %(min)d znaków " -"(ma długość %(length)d)." +"Upewnij się, że ta wartość ma co najmniej %(min)d znaków (ma długość %" +"(length)d)." #: newforms/fields.py:158 newforms/fields.py:187 newforms/fields.py:216 #, python-format @@ -3578,7 +3568,7 @@ msgstr "Wpisz poprawną datę/godzinę." msgid "No file was submitted." msgstr "Żaden plik nie został przesłany." -#: newforms/fields.py:440 oldforms/__init__.py:688 +#: newforms/fields.py:440 oldforms/__init__.py:689 msgid "The submitted file is empty." msgstr "Wysłany plik jest pusty." @@ -3613,7 +3603,7 @@ msgstr "Wprowadź poprawny adres IPv4." msgid "Select a valid choice. %s is not one of the available choices." msgstr "Wybierz poprawną wartość. %s nie jest jednym z dostępnych wyborów." -#: oldforms/__init__.py:408 +#: oldforms/__init__.py:409 #, python-format msgid "Ensure your text is less than %s character." msgid_plural "Ensure your text is less than %s characters." @@ -3621,32 +3611,32 @@ msgstr[0] "Upewnij się, że tekst ma mniej niż %s znak." msgstr[1] "Upewnij się, że tekst ma mniej niż %s znaki." msgstr[2] "Upewnij się, że tekst ma mniej niż %s znaków." -#: oldforms/__init__.py:413 +#: oldforms/__init__.py:414 msgid "Line breaks are not allowed here." msgstr "Znaki nowej linii są tutaj niedopuszczalne." -#: oldforms/__init__.py:511 oldforms/__init__.py:585 oldforms/__init__.py:624 +#: oldforms/__init__.py:512 oldforms/__init__.py:586 oldforms/__init__.py:625 #, python-format msgid "Select a valid choice; '%(data)s' is not in %(choices)s." msgstr "Wybierz poprawną opcję; '%(data)s' nie jest wśród %(choices)s." -#: oldforms/__init__.py:744 +#: oldforms/__init__.py:745 msgid "Enter a whole number between -32,768 and 32,767." msgstr "Proszę wpisać liczbę całkowitą z zakresu od -32 768 do 32 767" -#: oldforms/__init__.py:754 +#: oldforms/__init__.py:755 msgid "Enter a positive number." msgstr "Proszę wpisać liczbę dodatnią." -#: oldforms/__init__.py:764 +#: oldforms/__init__.py:765 msgid "Enter a whole number between 0 and 32,767." msgstr "Proszę wpisać liczbę całkowitą z zakresu od 0 do 32 767" -#: template/defaultfilters.py:555 +#: template/defaultfilters.py:655 msgid "yes,no,maybe" msgstr "tak,nie,może" -#: template/defaultfilters.py:585 +#: template/defaultfilters.py:686 #, python-format msgid "%(size)d byte" msgid_plural "%(size)d bytes" @@ -3654,17 +3644,17 @@ msgstr[0] "%(size)d bajt" msgstr[1] "%(size)d bajty" msgstr[2] "%(size)d bajtów" -#: template/defaultfilters.py:587 +#: template/defaultfilters.py:688 #, python-format msgid "%.1f KB" msgstr "%.1f KB" -#: template/defaultfilters.py:589 +#: template/defaultfilters.py:690 #, python-format msgid "%.1f MB" msgstr "%.1f MB" -#: template/defaultfilters.py:590 +#: template/defaultfilters.py:691 #, python-format msgid "%.1f GB" msgstr "%.1f GB" diff --git a/django/contrib/admin/media/css/base.css b/django/contrib/admin/media/css/base.css index 88f7d9a95a..9760d67dc4 100644 --- a/django/contrib/admin/media/css/base.css +++ b/django/contrib/admin/media/css/base.css @@ -4,11 +4,11 @@ */ /* Block IE 5 */ -@import "null?\"\{"; +@import "null.css?\"\{"; /* Import other styles */ @import url('global.css'); @import url('layout.css'); /* Import patch for IE 6 Windows */ -/*\*/ @import "patch-iewin.css"; /**/ \ No newline at end of file +/*\*/ @import "patch-iewin.css"; /**/ diff --git a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js index 36ae21411d..f6a39ca091 100644 --- a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js +++ b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js @@ -1,6 +1,16 @@ // Handles related-objects functionality: lookup link for raw_id_admin=True // and Add Another links. +function html_unescape(text) { + // Unescape a string that was escaped using django.utils.html.escape. + text = text.replace(/</g, '<'); + text = text.replace(/>/g, '>'); + text = text.replace(/"/g, '"'); + text = text.replace(/'/g, "'"); + text = text.replace(/&/g, '&'); + return text; +} + function showRelatedObjectLookupPopup(triggeringLink) { var name = triggeringLink.id.replace(/^lookup_/, ''); // IE doesn't like periods in the window name, so convert temporarily. @@ -42,6 +52,10 @@ function showAddAnotherPopup(triggeringLink) { } function dismissAddAnotherPopup(win, newId, newRepr) { + // newId and newRepr are expected to have previously been escaped by + // django.utils.html.escape. + newId = html_unescape(newId); + newRepr = html_unescape(newRepr); var name = win.name.replace(/___/g, '.'); var elem = document.getElementById(name); if (elem) { diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index b23013becd..a4e6269b6f 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -148,6 +148,8 @@ def items_for_result(cl, result): # function has an "allow_tags" attribute set to True. if not allow_tags: result_repr = escape(result_repr) + else: + result_repr = mark_safe(result_repr) else: field_val = getattr(result, f.attname) @@ -185,7 +187,7 @@ def items_for_result(cl, result): else: result_repr = escape(field_val) if force_unicode(result_repr) == '': - result_repr = ' ' + result_repr = mark_safe(' ') # If list_display_links not defined, add the link tag to the first field if (first and not cl.lookup_opts.admin.list_display_links) or field_name in cl.lookup_opts.admin.list_display_links: table_tag = {True:'th', False:'td'}[first] diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py index e5f31ba723..ef33bb33b0 100644 --- a/django/contrib/admin/templatetags/admin_modify.py +++ b/django/contrib/admin/templatetags/admin_modify.py @@ -118,7 +118,7 @@ class FieldWrapper(object): return not isinstance(self.field, models.AutoField) def header_class_attribute(self): - return self.field.blank and ' class="optional"' or '' + return self.field.blank and mark_safe(' class="optional"') or '' def use_raw_id_admin(self): return isinstance(self.field.rel, (models.ManyToOneRel, models.ManyToManyRel)) \ diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 947d09b852..9786935bf8 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -273,10 +273,9 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po post_url_continue += "?_popup=1" return HttpResponseRedirect(post_url_continue % pk_value) if "_popup" in request.POST: - if type(pk_value) is str: # Quote if string, so JavaScript doesn't think it's a variable. - pk_value = '"%s"' % pk_value.replace('"', '\\"') - return HttpResponse('' % \ - (pk_value, force_unicode(new_object).replace('"', '\\"'))) + return HttpResponse('' % \ + # escape() calls force_unicode. + (escape(pk_value), escape(new_object))) elif "_addanother" in request.POST: request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) return HttpResponseRedirect(request.path) diff --git a/django/core/cache/backends/locmem.py b/django/core/cache/backends/locmem.py index 5998f7bfd5..2d74e2b132 100644 --- a/django/core/cache/backends/locmem.py +++ b/django/core/cache/backends/locmem.py @@ -16,8 +16,12 @@ class CacheClass(SimpleCacheClass): def add(self, key, value, timeout=None): self._lock.writer_enters() + # Python 2.3 and 2.4 don't allow combined try-except-finally blocks. try: - SimpleCacheClass.add(self, key, value, timeout) + try: + super(CacheClass, self).add(key, pickle.dumps(value), timeout) + except pickle.PickleError: + pass finally: self._lock.writer_leaves() @@ -49,6 +53,7 @@ class CacheClass(SimpleCacheClass): def set(self, key, value, timeout=None): self._lock.writer_enters() + # Python 2.3 and 2.4 don't allow combined try-except-finally blocks. try: try: super(CacheClass, self).set(key, pickle.dumps(value), timeout) diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index dce2fd493d..fcbc9d1110 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -52,7 +52,7 @@ def load_command_class(app_name, name): return getattr(__import__('%s.management.commands.%s' % (app_name, name), {}, {}, ['Command']), 'Command')() -def get_commands(load_user_commands=True, project_directory=None): +def get_commands(): """ Returns a dictionary of commands against the application in which those commands can be found. This works by looking for a @@ -60,10 +60,10 @@ def get_commands(load_user_commands=True, project_directory=None): application -- if a commands package exists, all commands in that package are registered. - Core commands are always included; user-defined commands will also - be included if ``load_user_commands`` is True. If a project directory - is provided, the startproject command will be disabled, and the - startapp command will be modified to use that directory. + Core commands are always included. If a settings module has been + specified, user-defined commands will also be included, the + startproject command will be disabled, and the startapp command + will be modified to use the directory in which that module appears. The dictionary is in the format {command_name: app_name}. Key-value pairs from this dictionary can then be used in calls to @@ -80,16 +80,27 @@ def get_commands(load_user_commands=True, project_directory=None): if _commands is None: _commands = dict([(name, 'django.core') for name in find_commands(__path__[0])]) - if load_user_commands: - # Get commands from all installed apps. + # Get commands from all installed apps. + try: from django.conf import settings - for app_name in settings.INSTALLED_APPS: - try: - path = find_management_module(app_name) - _commands.update(dict([(name, app_name) - for name in find_commands(path)])) - except ImportError: - pass # No management module - ignore this app + apps = settings.INSTALLED_APPS + except (AttributeError, EnvironmentError): + apps = [] + + for app_name in apps: + try: + path = find_management_module(app_name) + _commands.update(dict([(name, app_name) + for name in find_commands(path)])) + except ImportError: + pass # No management module - ignore this app + + # Try to determine the project directory + try: + from django.conf import settings + project_directory = setup_environ(__import__(settings.SETTINGS_MODULE)) + except (AttributeError, EnvironmentError, ImportError): + project_directory = None if project_directory: # Remove the "startproject" command from self.commands, because @@ -146,8 +157,6 @@ class ManagementUtility(object): def __init__(self, argv=None): self.argv = argv or sys.argv[:] self.prog_name = os.path.basename(self.argv[0]) - self.project_directory = None - self.user_commands = False def main_help_text(self): """ @@ -159,8 +168,7 @@ class ManagementUtility(object): usage.append("Type '%s help ' for help on a specific" " subcommand." % self.prog_name) usage.append('Available subcommands:') - commands = get_commands(self.user_commands, - self.project_directory).keys() + commands = get_commands().keys() commands.sort() for cmd in commands: usage.append(' %s' % cmd) @@ -173,8 +181,7 @@ class ManagementUtility(object): django-admin.py or manage.py) if it can't be found. """ try: - app_name = get_commands(self.user_commands, - self.project_directory)[subcommand] + app_name = get_commands()[subcommand] if isinstance(app_name, BaseCommand): # If the command is already loaded, use it directly. klass = app_name @@ -235,8 +242,6 @@ class ProjectManagementUtility(ManagementUtility): """ def __init__(self, argv, project_directory): super(ProjectManagementUtility, self).__init__(argv) - self.project_directory = project_directory - self.user_commands = True def setup_environ(settings_mod): """ diff --git a/django/core/paginator.py b/django/core/paginator.py index b50ca826c4..71a5479fd5 100644 --- a/django/core/paginator.py +++ b/django/core/paginator.py @@ -91,7 +91,7 @@ class ObjectPaginator(object): a template for loop. """ if self._page_range is None: - self._page_range = range(1, self._pages + 1) + self._page_range = range(1, self.pages + 1) return self._page_range hits = property(_get_hits) diff --git a/django/middleware/gzip.py b/django/middleware/gzip.py index aa2a8ea5a6..62a2456b97 100644 --- a/django/middleware/gzip.py +++ b/django/middleware/gzip.py @@ -1,4 +1,5 @@ import re + from django.utils.text import compress_string from django.utils.cache import patch_vary_headers @@ -11,18 +12,21 @@ class GZipMiddleware(object): on the Accept-Encoding header. """ def process_response(self, request, response): + # It's not worth compressing non-OK or really short responses. if response.status_code != 200 or len(response.content) < 200: - # Not worth compressing really short responses or 304 status - # responses, etc. return response patch_vary_headers(response, ('Accept-Encoding',)) - # Avoid gzipping if we've already got a content-encoding or if the - # content-type is Javascript and the user's browser is IE. - is_js = ("msie" in request.META.get('HTTP_USER_AGENT', '').lower() and - "javascript" in response.get('Content-Type', '').lower()) - if response.has_header('Content-Encoding') or is_js: + # Avoid gzipping if we've already got a content-encoding. + if response.has_header('Content-Encoding'): + return response + + # Older versions of IE have issues with gzipped javascript. + # See http://code.djangoproject.com/ticket/2449 + is_ie = "msie" in request.META.get('HTTP_USER_AGENT', '').lower() + is_js = "javascript" in response.get('Content-Type', '').lower() + if is_ie and is_js: return response ae = request.META.get('HTTP_ACCEPT_ENCODING', '') diff --git a/django/newforms/extras/widgets.py b/django/newforms/extras/widgets.py index 60936a6bd6..0097ba3f54 100644 --- a/django/newforms/extras/widgets.py +++ b/django/newforms/extras/widgets.py @@ -6,6 +6,7 @@ import datetime from django.newforms.widgets import Widget, Select from django.utils.dates import MONTHS +from django.utils.safestring import mark_safe __all__ = ('SelectDateWidget',) @@ -51,7 +52,7 @@ class SelectDateWidget(Widget): select_html = Select(choices=year_choices).render(self.year_field % name, year_val) output.append(select_html) - return u'\n'.join(output) + return mark_safe(u'\n'.join(output)) def value_from_datadict(self, data, files, name): y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name) diff --git a/django/newforms/models.py b/django/newforms/models.py index c86b9b3a42..51ed16ff7f 100644 --- a/django/newforms/models.py +++ b/django/newforms/models.py @@ -3,13 +3,13 @@ Helper functions for creating Form classes from Django models and database field objects. """ -from django.utils.translation import ugettext +from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_unicode from django.utils.datastructures import SortedDict from util import ValidationError from forms import BaseForm -from fields import Field, ChoiceField +from fields import Field, ChoiceField, EMPTY_VALUES from widgets import Select, SelectMultiple, MultipleHiddenInput __all__ = ( @@ -151,15 +151,20 @@ class ModelChoiceField(ChoiceField): """A ChoiceField whose choices are a model QuerySet.""" # This class is a subclass of ChoiceField for purity, but it doesn't # actually use any of ChoiceField's implementation. + default_error_messages = { + 'invalid_choice': _(u'Select a valid choice. That choice is not one of' + u' the available choices.'), + } def __init__(self, queryset, empty_label=u"---------", cache_choices=False, required=True, widget=Select, label=None, initial=None, - help_text=None): + help_text=None, *args, **kwargs): self.empty_label = empty_label self.cache_choices = cache_choices # Call Field instead of ChoiceField __init__() because we don't need # ChoiceField.__init__(). - Field.__init__(self, required, widget, label, initial, help_text) + Field.__init__(self, required, widget, label, initial, help_text, + *args, **kwargs) self.queryset = queryset def _get_queryset(self): @@ -195,41 +200,43 @@ class ModelChoiceField(ChoiceField): def clean(self, value): Field.clean(self, value) - if value in ('', None): + if value in EMPTY_VALUES: return None try: value = self.queryset.get(pk=value) except self.queryset.model.DoesNotExist: - raise ValidationError(ugettext(u'Select a valid choice. That' - u' choice is not one of the' - u' available choices.')) + raise ValidationError(self.error_messages['invalid_choice']) return value class ModelMultipleChoiceField(ModelChoiceField): """A MultipleChoiceField whose choices are a model QuerySet.""" hidden_widget = MultipleHiddenInput + default_error_messages = { + 'list': _(u'Enter a list of values.'), + 'invalid_choice': _(u'Select a valid choice. %s is not one of the' + u' available choices.'), + } def __init__(self, queryset, cache_choices=False, required=True, widget=SelectMultiple, label=None, initial=None, - help_text=None): + help_text=None, *args, **kwargs): super(ModelMultipleChoiceField, self).__init__(queryset, None, - cache_choices, required, widget, label, initial, help_text) + cache_choices, required, widget, label, initial, help_text, + *args, **kwargs) def clean(self, value): if self.required and not value: - raise ValidationError(ugettext(u'This field is required.')) + raise ValidationError(self.error_messages['required']) elif not self.required and not value: return [] if not isinstance(value, (list, tuple)): - raise ValidationError(ugettext(u'Enter a list of values.')) + raise ValidationError(self.error_messages['list']) final_values = [] for val in value: try: obj = self.queryset.get(pk=val) except self.queryset.model.DoesNotExist: - raise ValidationError(ugettext(u'Select a valid choice. %s is' - u' not one of the available' - u' choices.') % val) + raise ValidationError(self.error_messages['invalid_choice'] % val) else: final_values.append(obj) return final_values diff --git a/django/newforms/widgets.py b/django/newforms/widgets.py index 350b878af9..580834857e 100644 --- a/django/newforms/widgets.py +++ b/django/newforms/widgets.py @@ -11,7 +11,7 @@ import copy from itertools import chain from django.utils.datastructures import MultiValueDict -from django.utils.html import escape +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 @@ -155,7 +155,7 @@ class Textarea(Widget): value = force_unicode(value) final_attrs = self.build_attrs(attrs, name=name) return mark_safe(u'%s' % (flatatt(final_attrs), - escape(value))) + conditional_escape(force_unicode(value)))) class DateTimeInput(Input): input_type = 'text' @@ -217,7 +217,9 @@ class Select(Widget): for option_value, option_label in chain(self.choices, choices): option_value = force_unicode(option_value) selected_html = (option_value == str_value) and u' selected="selected"' or '' - output.append(u'' % (escape(option_value), selected_html, escape(force_unicode(option_label)))) + output.append(u'' % ( + escape(option_value), selected_html, + conditional_escape(force_unicode(option_label)))) output.append(u'') return mark_safe(u'\n'.join(output)) @@ -254,7 +256,9 @@ class SelectMultiple(Widget): for option_value, option_label in chain(self.choices, choices): option_value = force_unicode(option_value) selected_html = (option_value in str_values) and ' selected="selected"' or '' - output.append(u'' % (escape(option_value), selected_html, escape(force_unicode(option_label)))) + output.append(u'' % ( + escape(option_value), selected_html, + conditional_escape(force_unicode(option_label)))) output.append(u'') return mark_safe(u'\n'.join(output)) @@ -278,7 +282,7 @@ class RadioInput(StrAndUnicode): def __unicode__(self): return mark_safe(u'' % (self.tag(), - self.choice_label)) + conditional_escape(force_unicode(self.choice_label)))) def is_checked(self): return self.value == self.choice_value @@ -317,11 +321,13 @@ class RadioFieldRenderer(StrAndUnicode): % force_unicode(w) for w in self])) class RadioSelect(Select): + renderer = RadioFieldRenderer def __init__(self, *args, **kwargs): - self.renderer = kwargs.pop('renderer', None) - if not self.renderer: - self.renderer = RadioFieldRenderer + # Override the default renderer if we were passed one. + renderer = kwargs.pop('renderer', None) + if renderer: + self.renderer = renderer super(RadioSelect, self).__init__(*args, **kwargs) def get_renderer(self, name, value, attrs=None, choices=()): @@ -361,7 +367,8 @@ class CheckboxSelectMultiple(SelectMultiple): cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values) option_value = force_unicode(option_value) rendered_cb = cb.render(name, option_value) - output.append(u'
  • ' % (rendered_cb, escape(force_unicode(option_label)))) + output.append(u'
  • ' % (rendered_cb, + conditional_escape(force_unicode(option_label)))) output.append(u'') return mark_safe(u'\n'.join(output)) diff --git a/django/template/__init__.py b/django/template/__init__.py index 761c08d6c9..c68a4b544d 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -547,9 +547,9 @@ class FilterExpression(object): if var == None: var, constant, i18n_constant = match.group("var", "constant", "i18n_constant") if i18n_constant: - var = '"%s"' % _(i18n_constant) + var = '"%s"' % _(i18n_constant.replace(r'\"', '"')) elif constant: - var = '"%s"' % constant + var = '"%s"' % constant.replace(r'\"', '"') upto = match.end() if var == None: raise TemplateSyntaxError, "Could not find variable at start of %s" % token diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 7d4a72efb3..e62e2e3eaf 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -25,6 +25,8 @@ def stringfilter(func): if args: args = list(args) args[0] = force_unicode(args[0]) + if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False): + return mark_safe(func(*args, **kwargs)) return func(*args, **kwargs) # Include a reference to the real function (used to check original @@ -106,6 +108,7 @@ floatformat.is_safe = True def iriencode(value): """Escapes an IRI value for use in a URL.""" return force_unicode(iri_to_uri(value)) +iriencode.is_safe = True iriencode = stringfilter(iriencode) def linenumbers(value, autoescape=None): diff --git a/django/test/testcases.py b/django/test/testcases.py index 2aa0a0783d..1d65ee1d23 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -51,9 +51,9 @@ class TestCase(unittest.TestCase): def _pre_setup(self): """Performs any pre-test setup. This includes: - * If the Test Case class has a 'fixtures' member, clearing the - database and installing the named fixtures at the start of each - test. + * Flushing the database. + * If the Test Case class has a 'fixtures' member, installing the + named fixtures. * Clearing the mail test outbox. """ call_command('flush', verbosity=0, interactive=False) diff --git a/django/utils/cache.py b/django/utils/cache.py index ae4de6dd87..5654bed7aa 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -20,6 +20,10 @@ An example: i18n middleware would need to distinguish caches by the import md5 import re import time +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback from django.conf import settings from django.core.cache import cache @@ -70,8 +74,6 @@ def patch_cache_control(response, **kwargs): cc = ', '.join([dictvalue(el) for el in cc.items()]) response['Cache-Control'] = cc -vary_delim_re = re.compile(r',\s*') - def patch_response_headers(response, cache_timeout=None): """ Adds some useful headers to the given HttpResponse object: @@ -109,14 +111,15 @@ def patch_vary_headers(response, newheaders): # Note that we need to keep the original order intact, because cache # implementations may rely on the order of the Vary contents in, say, # computing an MD5 hash. - vary = [] if response.has_header('Vary'): - vary = vary_delim_re.split(response['Vary']) - oldheaders = dict([(el.lower(), 1) for el in vary]) - for newheader in newheaders: - if not newheader.lower() in oldheaders: - vary.append(newheader) - response['Vary'] = ', '.join(vary) + vary_headers = cc_delim_re.split(response['Vary']) + else: + vary_headers = [] + # Use .lower() here so we treat headers as case-insensitive. + existing_headers = set([header.lower() for header in vary_headers]) + additional_headers = [newheader for newheader in newheaders + if newheader.lower() not in existing_headers] + response['Vary'] = ', '.join(vary_headers + additional_headers) def _generate_cache_key(request, headerlist, key_prefix): """Returns a cache key from the headers given in the header list.""" @@ -169,7 +172,7 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None): key_prefix, iri_to_uri(request.path)) if response.has_header('Vary'): headerlist = ['HTTP_'+header.upper().replace('-', '_') - for header in vary_delim_re.split(response['Vary'])] + for header in cc_delim_re.split(response['Vary'])] cache.set(cache_key, headerlist, cache_timeout) return _generate_cache_key(request, headerlist, key_prefix) else: diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 549aa3f183..ffdc73f922 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -7,9 +7,9 @@ class MergeDict(object): self.dicts = dicts def __getitem__(self, key): - for dict in self.dicts: + for dict_ in self.dicts: try: - return dict[key] + return dict_[key] except KeyError: pass raise KeyError @@ -24,22 +24,22 @@ class MergeDict(object): return default def getlist(self, key): - for dict in self.dicts: + for dict_ in self.dicts: try: - return dict.getlist(key) + return dict_.getlist(key) except KeyError: pass raise KeyError def items(self): item_list = [] - for dict in self.dicts: - item_list.extend(dict.items()) + for dict_ in self.dicts: + item_list.extend(dict_.items()) return item_list def has_key(self, key): - for dict in self.dicts: - if key in dict: + for dict_ in self.dicts: + if key in dict_: return True return False @@ -56,7 +56,7 @@ class SortedDict(dict): def __init__(self, data=None): if data is None: data = {} - dict.__init__(self, data) + super(SortedDict, self).__init__(data) if isinstance(data, dict): self.keyOrder = data.keys() else: @@ -68,12 +68,12 @@ class SortedDict(dict): for key, value in self.iteritems()]) def __setitem__(self, key, value): - dict.__setitem__(self, key, value) + super(SortedDict, self).__setitem__(key, value) if key not in self.keyOrder: self.keyOrder.append(key) def __delitem__(self, key): - dict.__delitem__(self, key) + super(SortedDict, self).__delitem__(key) self.keyOrder.remove(key) def __iter__(self): @@ -81,7 +81,7 @@ class SortedDict(dict): yield k def pop(self, k, *args): - result = dict.pop(self, k, *args) + result = super(SortedDict, self).pop(k, *args) try: self.keyOrder.remove(k) except ValueError: @@ -90,7 +90,7 @@ class SortedDict(dict): return result def popitem(self): - result = dict.popitem(self) + result = super(SortedDict, self).popitem() self.keyOrder.remove(result[0]) return result @@ -99,7 +99,7 @@ class SortedDict(dict): def iteritems(self): for key in self.keyOrder: - yield key, dict.__getitem__(self, key) + yield key, super(SortedDict, self).__getitem__(key) def keys(self): return self.keyOrder[:] @@ -108,20 +108,20 @@ class SortedDict(dict): return iter(self.keyOrder) def values(self): - return [dict.__getitem__(self, k) for k in self.keyOrder] + return [super(SortedDict, self).__getitem__(k) for k in self.keyOrder] def itervalues(self): for key in self.keyOrder: - yield dict.__getitem__(self, key) + yield super(SortedDict, self).__getitem__(key) - def update(self, dict): - for k, v in dict.items(): + def update(self, dict_): + for k, v in dict_.items(): self.__setitem__(k, v) def setdefault(self, key, default): if key not in self.keyOrder: self.keyOrder.append(key) - return dict.setdefault(self, key, default) + return super(SortedDict, self).setdefault(key, default) def value_for_index(self, index): """Returns the value of the item at the given zero-based index.""" @@ -135,7 +135,7 @@ class SortedDict(dict): if n < index: index -= 1 self.keyOrder.insert(index, key) - dict.__setitem__(self, key, value) + super(SortedDict, self).__setitem__(key, value) def copy(self): """Returns a copy of this object.""" @@ -173,10 +173,11 @@ class MultiValueDict(dict): single name-value pairs. """ def __init__(self, key_to_list_mapping=()): - dict.__init__(self, key_to_list_mapping) + super(MultiValueDict, self).__init__(key_to_list_mapping) def __repr__(self): - return "<%s: %s>" % (self.__class__.__name__, dict.__repr__(self)) + return "<%s: %s>" % (self.__class__.__name__, + super(MultiValueDict, self).__repr__()) def __getitem__(self, key): """ @@ -184,7 +185,7 @@ class MultiValueDict(dict): raises KeyError if not found. """ try: - list_ = dict.__getitem__(self, key) + list_ = super(MultiValueDict, self).__getitem__(key) except KeyError: raise MultiValueDictKeyError, "Key %r not found in %r" % (key, self) try: @@ -193,10 +194,10 @@ class MultiValueDict(dict): return [] def __setitem__(self, key, value): - dict.__setitem__(self, key, [value]) + super(MultiValueDict, self).__setitem__(key, [value]) def __copy__(self): - return self.__class__(dict.items(self)) + return self.__class__(super(MultiValueDict, self).items()) def __deepcopy__(self, memo=None): import copy @@ -210,7 +211,10 @@ class MultiValueDict(dict): return result def get(self, key, default=None): - """Returns the default value if the requested data doesn't exist.""" + """ + Returns the last data value for the passed key. If key doesn't exist + or value is an empty list, then default is returned. + """ try: val = self[key] except KeyError: @@ -220,14 +224,17 @@ class MultiValueDict(dict): return val def getlist(self, key): - """Returns an empty list if the requested data doesn't exist.""" + """ + Returns the list of values for the passed key. If key doesn't exist, + then an empty list is returned. + """ try: - return dict.__getitem__(self, key) + return super(MultiValueDict, self).__getitem__(key) except KeyError: return [] def setlist(self, key, list_): - dict.__setitem__(self, key, list_) + super(MultiValueDict, self).__setitem__(key, list_) def setdefault(self, key, default=None): if key not in self: @@ -242,7 +249,7 @@ class MultiValueDict(dict): def appendlist(self, key, value): """Appends an item to the internal list associated with key.""" self.setlistdefault(key, []) - dict.__setitem__(self, key, self.getlist(key) + [value]) + super(MultiValueDict, self).__setitem__(key, self.getlist(key) + [value]) def items(self): """ @@ -253,7 +260,7 @@ class MultiValueDict(dict): def lists(self): """Returns a list of (key, list) pairs.""" - return dict.items(self) + return super(MultiValueDict, self).items() def values(self): """Returns a list of the last value on every key list.""" @@ -315,7 +322,7 @@ class DotExpandedDict(dict): try: current[bits[-1]] = v except TypeError: # Special-case if current isn't a dict. - current = {bits[-1] : v} + current = {bits[-1]: v} class FileDict(dict): """ diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index c95c842a4f..a7259b3ce5 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -168,10 +168,9 @@ def translation(language): res.merge(t) return res - if hasattr(settings, 'LOCALE_PATHS'): - for localepath in settings.LOCALE_PATHS: - if os.path.isdir(localepath): - res = _merge(localepath) + for localepath in settings.LOCALE_PATHS: + if os.path.isdir(localepath): + res = _merge(localepath) if projectpath and os.path.isdir(projectpath): res = _merge(projectpath) diff --git a/django/views/debug.py b/django/views/debug.py index 7c45af230a..3358d2f08e 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -422,11 +422,11 @@ TECHNICAL_500_TEMPLATE = """ {% if frame.context_line %}
    {% if frame.pre_context %} -
      {% for line in frame.pre_context %}
    1. {{ line }}
    2. {% endfor %}
    +
      {% for line in frame.pre_context %}
    1. {{ line|escape }}
    2. {% endfor %}
    {% endif %} -
    1. {{ frame.context_line }} ...
    +
    1. {{ frame.context_line|escape }} ...
    {% if frame.post_context %} -
      {% for line in frame.post_context %}
    1. {{ line }}
    2. {% endfor %}
    +
      {% for line in frame.post_context %}
    1. {{ line|escape }}
    2. {% endfor %}
    {% endif %}
    {% endif %} @@ -445,8 +445,8 @@ TECHNICAL_500_TEMPLATE = """ {% for var in frame.vars|dictsort:"0" %} - {{ var.0 }} -
    {{ var.1|pprint }}
    + {{ var.0|escape }} +
    {{ var.1|pprint|escape }}
    {% endfor %} @@ -466,7 +466,7 @@ Traceback (most recent call last):
    {% for frame in frames %} File "{{ frame.filename }}" in {{ frame.function }}
    {% if frame.context_line %} -   {{ frame.lineno }}. {{ frame.context_line }}
    +   {{ frame.lineno }}. {{ frame.context_line|escape }}
    {% endif %} {% endfor %}
      {{ exception_type }} at {{ request.path|escape }}
    diff --git a/docs/i18n.txt b/docs/i18n.txt index 2c43e7884e..8beb2188e8 100644 --- a/docs/i18n.txt +++ b/docs/i18n.txt @@ -658,7 +658,7 @@ message file. The choice is yours. of the settings file to determine this, and a settings file doesn't exist if you're manually configuring your settings.) -.. _settings documentation: ../settings/#using-settings-without-the-django-settings-module-environment-variable +.. _settings documentation: ../settings/#using-settings-without-setting-django-settings-module All message file repositories are structured the same way. They are: diff --git a/docs/settings.txt b/docs/settings.txt index 6241749753..0219f9853a 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -583,7 +583,7 @@ LOCALE_PATHS Default: ``()`` (Empty tuple) -A list of directories where Django looks for translation files. +A tuple of directories where Django looks for translation files. See the `internationalization docs section`_ explaining the variable and the default behavior. diff --git a/docs/templates_python.txt b/docs/templates_python.txt index e4658f6461..5ac93f5a58 100644 --- a/docs/templates_python.txt +++ b/docs/templates_python.txt @@ -755,61 +755,106 @@ inside the template code: ``EscapeString`` and ``EscapeUnicode``. You will not normally need to worry about these; they exist for the implementation of the ``escape`` filter. -Inside your filter, you will need to think about three areas in order to be -auto-escaping compliant: +When you are writing a filter, your code will typically fall into one of two +situations: - 1. If your filter returns a string that is ready for direct output (it should - be considered a "safe" string), you should call - ``django.utils.safestring.mark_safe()`` on the result prior to returning. - This will turn the result into the appropriate ``SafeData`` type. This is - often the case when you are returning raw HTML, for example. + 1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``, + ``'``, ``"`` or ``&``) into the result that were not already present. In + this case, you can let Django take care of all the auto-escaping handling + for you. All you need to do is put the ``is_safe`` attribute on your + filter function and set it to ``True``. This attribute tells Django that + is a "safe" string is passed into your filter, the result will still be + "safe" and if a non-safe string is passed in, Django will automatically + escape it, if necessary. The reason ``is_safe`` is necessary is because + there are plenty of normal string operations that will turn a ``SafeData`` + object back into a normal ``str`` or ``unicode`` object and, rather than + try to catch them all, which would be very difficult, Django repairs the + damage after the filter has completed. - 2. If your filter is given a "safe" string, is it guaranteed to return a - "safe" string? If so, set the ``is_safe`` attribute on the function to be - ``True``. For example, a filter that replaced a word consisting only of - digits with the number spelt out in words is going to be - safe-string-preserving, since it cannot introduce any of the five dangerous - characters: <, >, ", ' or &. We can write:: + For example, suppose you have a filter that adds the string ``xx`` to the + end of any input. Since this introduces no dangerous HTML characters into + the result (aside from any that were already present), you should mark + your filter with ``is_safe``:: @register.filter - def convert_to_words(value): - # ... implementation here ... - return result + def add_xx(value): + return '%sxx' % value + add_xx.is_safe = True - convert_to_words.is_safe = True + When this filter is used in a template where auto-escaping is enabled, + Django will escape the output whenever the input is not already marked as + "safe". - Note that this filter does not return a universally safe result (it does - not return ``mark_safe(result)``) because if it is handed a raw string such - as '', this will need further escaping in an auto-escape environment. - The ``is_safe`` attribute only talks about the the result when a safe - string is passed into the filter. + By default, ``is_safe`` defaults to ``False`` and you can omit it from + any filters where it isn't required. - 3. Will your filter behave differently depending upon whether auto-escaping - is currently in effect or not? This is normally a concern when you are - returning mixed content (HTML elements mixed with user-supplied content). - For example, the ``ordered_list`` filter that ships with Django needs to - know whether to escape its content or not. It will always return a safe - string. Since it returns raw HTML, we cannot apply escaping to the - result -- it needs to be done in-situ. + Be careful when deciding if your filter really does leave safe strings + as safe. Sometimes if you are *removing* characters, you can + inadvertently leave unbalanced HTML tags or entities in the result. + For example, removing a ``>`` from the input might turn ```` into + ``>> paginator.pages 2 -# The paginator can provide a list of all available pages +# The paginator can provide a list of all available pages. +>>> paginator = ObjectPaginator(Article.objects.all(), 10) >>> paginator.page_range [1, 2] """} diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index 3879da7703..e94ea33139 100644 --- a/tests/regressiontests/cache/tests.py +++ b/tests/regressiontests/cache/tests.py @@ -3,9 +3,12 @@ # Unit tests for cache framework # Uses whatever cache backend is set in the test settings file. -from django.core.cache import cache import time, unittest +from django.core.cache import cache +from django.utils.cache import patch_vary_headers +from django.http import HttpResponse + # functions/classes for complex data type tests def f(): return 42 @@ -87,5 +90,30 @@ class Cache(unittest.TestCase): cache.set(key, value) self.assertEqual(cache.get(key), value) + +class CacheUtils(unittest.TestCase): + """TestCase for django.utils.cache functions.""" + + def test_patch_vary_headers(self): + headers = ( + # Initial vary, new headers, resulting vary. + (None, ('Accept-Encoding',), 'Accept-Encoding'), + ('Accept-Encoding', ('accept-encoding',), 'Accept-Encoding'), + ('Accept-Encoding', ('ACCEPT-ENCODING',), 'Accept-Encoding'), + ('Cookie', ('Accept-Encoding',), 'Cookie, Accept-Encoding'), + ('Cookie, Accept-Encoding', ('Accept-Encoding',), 'Cookie, Accept-Encoding'), + ('Cookie, Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'), + (None, ('Accept-Encoding', 'COOKIE'), 'Accept-Encoding, COOKIE'), + ('Cookie, Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'), + ('Cookie , Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'), + ) + for initial_vary, newheaders, resulting_vary in headers: + response = HttpResponse() + if initial_vary is not None: + response['Vary'] = initial_vary + patch_vary_headers(response, newheaders) + self.assertEqual(response['Vary'], resulting_vary) + + if __name__ == '__main__': unittest.main() diff --git a/tests/regressiontests/datastructures/tests.py b/tests/regressiontests/datastructures/tests.py index d1e21e673c..3b0ccde257 100644 --- a/tests/regressiontests/datastructures/tests.py +++ b/tests/regressiontests/datastructures/tests.py @@ -25,11 +25,23 @@ >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']}) >>> d['name'] 'Simon' +>>> d.get('name') +'Simon' >>> d.getlist('name') ['Adrian', 'Simon'] +>>> d['lastname'] +Traceback (most recent call last): +... +MultiValueDictKeyError: "Key 'lastname' not found in " +>>> d.get('lastname') + >>> d.get('lastname', 'nonexistent') 'nonexistent' +>>> d.getlist('lastname') +[] >>> d.setlist('lastname', ['Holovaty', 'Willison']) +>>> d.getlist('lastname') +['Holovaty', 'Willison'] ### SortedDict ################################################################# diff --git a/tests/regressiontests/forms/error_messages.py b/tests/regressiontests/forms/error_messages.py new file mode 100644 index 0000000000..9f972f5b90 --- /dev/null +++ b/tests/regressiontests/forms/error_messages.py @@ -0,0 +1,360 @@ +# -*- coding: utf-8 -*- +tests = r""" +>>> from django.newforms import * + +# CharField ################################################################### + +>>> e = {'required': 'REQUIRED'} +>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s' +>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s' +>>> f = CharField(min_length=5, max_length=10, error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('1234') +Traceback (most recent call last): +... +ValidationError: [u'LENGTH 4, MIN LENGTH 5'] +>>> f.clean('12345678901') +Traceback (most recent call last): +... +ValidationError: [u'LENGTH 11, MAX LENGTH 10'] + +# IntegerField ################################################################ + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> e['min_value'] = 'MIN VALUE IS %s' +>>> e['max_value'] = 'MAX VALUE IS %s' +>>> f = IntegerField(min_value=5, max_value=10, error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abc') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] +>>> f.clean('4') +Traceback (most recent call last): +... +ValidationError: [u'MIN VALUE IS 5'] +>>> f.clean('11') +Traceback (most recent call last): +... +ValidationError: [u'MAX VALUE IS 10'] + +# FloatField ################################################################## + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> e['min_value'] = 'MIN VALUE IS %s' +>>> e['max_value'] = 'MAX VALUE IS %s' +>>> f = FloatField(min_value=5, max_value=10, error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abc') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] +>>> f.clean('4') +Traceback (most recent call last): +... +ValidationError: [u'MIN VALUE IS 5'] +>>> f.clean('11') +Traceback (most recent call last): +... +ValidationError: [u'MAX VALUE IS 10'] + +# DecimalField ################################################################ + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> e['min_value'] = 'MIN VALUE IS %s' +>>> e['max_value'] = 'MAX VALUE IS %s' +>>> e['max_digits'] = 'MAX DIGITS IS %s' +>>> e['max_decimal_places'] = 'MAX DP IS %s' +>>> e['max_whole_digits'] = 'MAX DIGITS BEFORE DP IS %s' +>>> f = DecimalField(min_value=5, max_value=10, error_messages=e) +>>> f2 = DecimalField(max_digits=4, decimal_places=2, error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abc') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] +>>> f.clean('4') +Traceback (most recent call last): +... +ValidationError: [u'MIN VALUE IS 5'] +>>> f.clean('11') +Traceback (most recent call last): +... +ValidationError: [u'MAX VALUE IS 10'] +>>> f2.clean('123.45') +Traceback (most recent call last): +... +ValidationError: [u'MAX DIGITS IS 4'] +>>> f2.clean('1.234') +Traceback (most recent call last): +... +ValidationError: [u'MAX DP IS 2'] +>>> f2.clean('123.4') +Traceback (most recent call last): +... +ValidationError: [u'MAX DIGITS BEFORE DP IS 2'] + +# DateField ################################################################### + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> f = DateField(error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abc') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] + +# TimeField ################################################################### + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> f = TimeField(error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abc') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] + +# DateTimeField ############################################################### + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> f = DateTimeField(error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abc') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] + +# RegexField ################################################################## + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s' +>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s' +>>> f = RegexField(r'^\d+$', min_length=5, max_length=10, error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abcde') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] +>>> f.clean('1234') +Traceback (most recent call last): +... +ValidationError: [u'LENGTH 4, MIN LENGTH 5'] +>>> f.clean('12345678901') +Traceback (most recent call last): +... +ValidationError: [u'LENGTH 11, MAX LENGTH 10'] + +# EmailField ################################################################## + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s' +>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s' +>>> f = EmailField(min_length=8, max_length=10, error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abcdefgh') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] +>>> f.clean('a@b.com') +Traceback (most recent call last): +... +ValidationError: [u'LENGTH 7, MIN LENGTH 8'] +>>> f.clean('aye@bee.com') +Traceback (most recent call last): +... +ValidationError: [u'LENGTH 11, MAX LENGTH 10'] + +# FileField ################################################################## + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> e['missing'] = 'MISSING' +>>> e['empty'] = 'EMPTY FILE' +>>> f = FileField(error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abc') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] +>>> f.clean({}) +Traceback (most recent call last): +... +ValidationError: [u'MISSING'] +>>> f.clean({'filename': 'name', 'content':''}) +Traceback (most recent call last): +... +ValidationError: [u'EMPTY FILE'] + +# URLField ################################################################## + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID' +>>> e['invalid_link'] = 'INVALID LINK' +>>> f = URLField(verify_exists=True, error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('abc.c') +Traceback (most recent call last): +... +ValidationError: [u'INVALID'] +>>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com') +Traceback (most recent call last): +... +ValidationError: [u'INVALID LINK'] + +# BooleanField ################################################################ + +>>> e = {'required': 'REQUIRED'} +>>> f = BooleanField(error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] + +# ChoiceField ################################################################# + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE' +>>> f = ChoiceField(choices=[('a', 'aye')], error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('b') +Traceback (most recent call last): +... +ValidationError: [u'b IS INVALID CHOICE'] + +# MultipleChoiceField ######################################################### + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE' +>>> e['invalid_list'] = 'NOT A LIST' +>>> f = MultipleChoiceField(choices=[('a', 'aye')], error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('b') +Traceback (most recent call last): +... +ValidationError: [u'NOT A LIST'] +>>> f.clean(['b']) +Traceback (most recent call last): +... +ValidationError: [u'b IS INVALID CHOICE'] + +# SplitDateTimeField ########################################################## + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid_date'] = 'INVALID DATE' +>>> e['invalid_time'] = 'INVALID TIME' +>>> f = SplitDateTimeField(error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean(['a', 'b']) +Traceback (most recent call last): +... +ValidationError: [u'INVALID DATE', u'INVALID TIME'] + +# IPAddressField ############################################################## + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid'] = 'INVALID IP ADDRESS' +>>> f = IPAddressField(error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('127.0.0') +Traceback (most recent call last): +... +ValidationError: [u'INVALID IP ADDRESS'] + +############################################################################### + +# Create choices for the model choice field tests below. + +>>> from regressiontests.forms.models import ChoiceModel +>>> ChoiceModel.objects.create(pk=1, name='a') + +>>> ChoiceModel.objects.create(pk=2, name='b') + +>>> ChoiceModel.objects.create(pk=3, name='c') + + +# ModelChoiceField ############################################################ + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid_choice'] = 'INVALID CHOICE' +>>> f = ModelChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('4') +Traceback (most recent call last): +... +ValidationError: [u'INVALID CHOICE'] + +# ModelMultipleChoiceField #################################################### + +>>> e = {'required': 'REQUIRED'} +>>> e['invalid_choice'] = '%s IS INVALID CHOICE' +>>> e['list'] = 'NOT A LIST OF VALUES' +>>> f = ModelMultipleChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'REQUIRED'] +>>> f.clean('3') +Traceback (most recent call last): +... +ValidationError: [u'NOT A LIST OF VALUES'] +>>> f.clean(['4']) +Traceback (most recent call last): +... +ValidationError: [u'4 IS INVALID CHOICE'] +""" diff --git a/tests/regressiontests/forms/models.py b/tests/regressiontests/forms/models.py index 1a6f566b6b..c7ce128560 100644 --- a/tests/regressiontests/forms/models.py +++ b/tests/regressiontests/forms/models.py @@ -10,6 +10,10 @@ class Defaults(models.Model): def_date = models.DateField(default = datetime.date(1980, 1, 1)) value = models.IntegerField(default=42) +class ChoiceModel(models.Model): + """For ModelChoiceField and ModelMultipleChoiceField tests.""" + name = models.CharField(max_length=10) + __test__ = {'API_TESTS': """ >>> from django.newforms import form_for_model, form_for_instance diff --git a/tests/regressiontests/forms/widgets.py b/tests/regressiontests/forms/widgets.py index cb1d084631..ea8cf135aa 100644 --- a/tests/regressiontests/forms/widgets.py +++ b/tests/regressiontests/forms/widgets.py @@ -2,6 +2,7 @@ tests = r""" >>> from django.newforms import * >>> from django.newforms.widgets import RadioFieldRenderer +>>> from django.utils.safestring import mark_safe >>> import datetime >>> import time >>> import re @@ -205,6 +206,8 @@ u'' u'' >>> w.render('msg', 'some "quoted" & ampersanded value') u'' +>>> w.render('msg', mark_safe('pre "quoted" value')) +u'' >>> w.render('msg', 'value', attrs={'class': 'pretty', 'rows': 20}) u'' @@ -375,6 +378,17 @@ If 'choices' is passed to both the constructor and render(), then they'll both b +# Choices are escaped correctly +>>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))) + + +# Unicode choices are correctly rendered as HTML >>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) u'' @@ -538,6 +552,17 @@ If 'choices' is passed to both the constructor and render(), then they'll both b +# Choices are escaped correctly +>>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))) + + +# Unicode choices are correctly rendered as HTML >>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) u'' @@ -663,6 +688,16 @@ You can create your own custom renderers for RadioSelect to use.
    +Or you can use custom RadioSelect fields that use your custom renderer. +>>> class CustomRadioSelect(RadioSelect): +... renderer = MyRenderer +>>> w = CustomRadioSelect() +>>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +
    +
    +
    + + A RadioFieldRenderer object also allows index access to individual RadioInput objects. >>> w = RadioSelect() @@ -682,6 +717,14 @@ Traceback (most recent call last): ... IndexError: list index out of range +# Choices are escaped correctly +>>> w = RadioSelect() +>>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))) +
      +
    • +
    • +
    + # Unicode choices are correctly rendered as HTML >>> w = RadioSelect() >>> unicode(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])) @@ -811,6 +854,17 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
  • +# Choices are escaped correctly +>>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))) +
      +
    • +
    • +
    • +
    • +
    • +
    + +# Unicode choices are correctly rendered as HTML >>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) u'
      \n
    • \n
    • \n
    • \n
    • \n
    • \n
    ' diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py index 27b24cb169..2a06703948 100644 --- a/tests/regressiontests/templates/filters.py +++ b/tests/regressiontests/templates/filters.py @@ -198,6 +198,12 @@ def get_filter_tests(): 'filter-phone2numeric01': ('{{ a|phone2numeric }} {{ b|phone2numeric }}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>") }, "<1-800-2255-63> <1-800-2255-63>"), 'filter-phone2numeric02': ('{% autoescape off %}{{ a|phone2numeric }} {{ b|phone2numeric }}{% endautoescape %}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>") }, "<1-800-2255-63> <1-800-2255-63>"), + # Ensure iriencode keeps safe strings: + 'filter-iriencode01': ('{{ url|iriencode }}', {'url': '?test=1&me=2'}, '?test=1&me=2'), + 'filter-iriencode02': ('{% autoescape off %}{{ url|iriencode }}{% endautoescape %}', {'url': '?test=1&me=2'}, '?test=1&me=2'), + 'filter-iriencode03': ('{{ url|iriencode }}', {'url': mark_safe('?test=1&me=2')}, '?test=1&me=2'), + 'filter-iriencode04': ('{% autoescape off %}{{ url|iriencode }}{% endautoescape %}', {'url': mark_safe('?test=1&me=2')}, '?test=1&me=2'), + # Chaining a bunch of safeness-preserving filters should not alter # the safe status either way. 'chaining01': ('{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}', {"a": "a < b", "b": mark_safe("a < b")}, " A < b . A < b "), diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 5c3a0a9081..f3c131dd91 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -268,6 +268,12 @@ class Templates(unittest.TestCase): # Embedded newlines make it not-a-tag. 'basic-syntax24': ("{{ moo\n }}", {}, "{{ moo\n }}"), + # Literal strings are permitted inside variables, mostly for i18n + # purposes. + 'basic-syntax25': ('{{ "fred" }}', {}, "fred"), + 'basic-syntax26': (r'{{ "\"fred\"" }}', {}, "\"fred\""), + 'basic-syntax27': (r'{{ _("\"fred\"") }}', {}, "\"fred\""), + # List-index syntax allows a template to access a certain item of a subscriptable object. 'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"),