diff --git a/.tx/config b/.tx/config
index cb617326b9..fdd7a576f7 100644
--- a/.tx/config
+++ b/.tx/config
@@ -2,77 +2,77 @@
host = https://www.transifex.com
lang_map = sr@latin:sr_Latn
-[django.core]
+[django-core.core]
file_filter = django/conf/locale//LC_MESSAGES/django.po
source_file = django/conf/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-admin]
+[django-core.contrib-admin]
file_filter = django/contrib/admin/locale//LC_MESSAGES/django.po
source_file = django/contrib/admin/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-admin-js]
+[django-core.contrib-admin-js]
file_filter = django/contrib/admin/locale//LC_MESSAGES/djangojs.po
source_file = django/contrib/admin/locale/en/LC_MESSAGES/djangojs.po
source_lang = en
-[django.contrib-admindocs]
+[django-core.contrib-admindocs]
file_filter = django/contrib/admindocs/locale//LC_MESSAGES/django.po
source_file = django/contrib/admindocs/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-auth]
+[django-core.contrib-auth]
file_filter = django/contrib/auth/locale//LC_MESSAGES/django.po
source_file = django/contrib/auth/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-comments]
+[django-core.contrib-comments]
file_filter = django/contrib/comments/locale//LC_MESSAGES/django.po
source_file = django/contrib/comments/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-contenttypes]
+[django-core.contrib-contenttypes]
file_filter = django/contrib/contenttypes/locale//LC_MESSAGES/django.po
source_file = django/contrib/contenttypes/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-flatpages]
+[django-core.contrib-flatpages]
file_filter = django/contrib/flatpages/locale//LC_MESSAGES/django.po
source_file = django/contrib/flatpages/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-formtools]
+[django-core.contrib-formtools]
file_filter = django/contrib/formtools/locale//LC_MESSAGES/django.po
source_file = django/contrib/formtools/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-gis]
+[django-core.contrib-gis]
file_filter = django/contrib/gis/locale//LC_MESSAGES/django.po
source_file = django/contrib/gis/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-humanize]
+[django-core.contrib-humanize]
file_filter = django/contrib/humanize/locale//LC_MESSAGES/django.po
source_file = django/contrib/humanize/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-messages]
+[django-core.contrib-messages]
file_filter = django/contrib/messages/locale//LC_MESSAGES/django.po
source_file = django/contrib/messages/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-redirects]
+[django-core.contrib-redirects]
file_filter = django/contrib/redirects/locale//LC_MESSAGES/django.po
source_file = django/contrib/redirects/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-sessions]
+[django-core.contrib-sessions]
file_filter = django/contrib/sessions/locale//LC_MESSAGES/django.po
source_file = django/contrib/sessions/locale/en/LC_MESSAGES/django.po
source_lang = en
-[django.contrib-sites]
+[django-core.contrib-sites]
file_filter = django/contrib/sites/locale//LC_MESSAGES/django.po
source_file = django/contrib/sites/locale/en/LC_MESSAGES/django.po
source_lang = en
diff --git a/MANIFEST.in b/MANIFEST.in
index 0e0aba1268..e9c5a409be 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -10,7 +10,9 @@ recursive-include docs *
recursive-include scripts *
recursive-include extras *
recursive-include tests *
+recursive-include django/conf/app_template *
recursive-include django/conf/locale *
+recursive-include django/conf/project_template *
recursive-include django/contrib/*/locale *
recursive-include django/contrib/admin/templates *
recursive-include django/contrib/admin/static *
diff --git a/django/bin/__init__.py b/django/bin/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/django/conf/locale/en/LC_MESSAGES/django.po b/django/conf/locale/en/LC_MESSAGES/django.po
index 2980c02ef7..4c94d5a00a 100644
--- a/django/conf/locale/en/LC_MESSAGES/django.po
+++ b/django/conf/locale/en/LC_MESSAGES/django.po
@@ -4,7 +4,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
+"POT-Creation-Date: 2013-05-02 16:17+0200\n"
"PO-Revision-Date: 2010-05-13 15:35+0200\n"
"Last-Translator: Django team\n"
"Language-Team: English \n"
@@ -337,7 +337,7 @@ msgstr ""
msgid "Enter a valid value."
msgstr ""
-#: core/validators.py:53 forms/fields.py:627
+#: core/validators.py:53 forms/fields.py:640
msgid "Enter a valid URL."
msgstr ""
@@ -408,7 +408,7 @@ msgstr[1] ""
msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s."
msgstr ""
-#: db/models/base.py:905 forms/models.py:590
+#: db/models/base.py:905 forms/models.py:605
msgid "and"
msgstr ""
@@ -630,46 +630,54 @@ msgstr ""
msgid "Enter a number."
msgstr ""
-#: forms/fields.py:295
-#, python-format
-msgid "Ensure that there are no more than %s digits in total."
-msgstr ""
-
#: forms/fields.py:296
#, python-format
-msgid "Ensure that there are no more than %s decimal places."
-msgstr ""
+msgid "Ensure that there are no more than %(max)s digit in total."
+msgid_plural "Ensure that there are no more than %(max)s digits in total."
+msgstr[0] ""
+msgstr[1] ""
-#: forms/fields.py:297
+#: forms/fields.py:300
#, python-format
-msgid "Ensure that there are no more than %s digits before the decimal point."
-msgstr ""
+msgid "Ensure that there are no more than %(max)s decimal place."
+msgid_plural "Ensure that there are no more than %(max)s decimal places."
+msgstr[0] ""
+msgstr[1] ""
-#: forms/fields.py:393 forms/fields.py:1045
+#: forms/fields.py:304
+#, python-format
+msgid ""
+"Ensure that there are no more than %(max)s digit before the decimal point."
+msgid_plural ""
+"Ensure that there are no more than %(max)s digits before the decimal point."
+msgstr[0] ""
+msgstr[1] ""
+
+#: forms/fields.py:406 forms/fields.py:1058
msgid "Enter a valid date."
msgstr ""
-#: forms/fields.py:417 forms/fields.py:1046
+#: forms/fields.py:430 forms/fields.py:1059
msgid "Enter a valid time."
msgstr ""
-#: forms/fields.py:438
+#: forms/fields.py:451
msgid "Enter a valid date/time."
msgstr ""
-#: forms/fields.py:512
+#: forms/fields.py:525
msgid "No file was submitted. Check the encoding type on the form."
msgstr ""
-#: forms/fields.py:513
+#: forms/fields.py:526
msgid "No file was submitted."
msgstr ""
-#: forms/fields.py:514
+#: forms/fields.py:527
msgid "The submitted file is empty."
msgstr ""
-#: forms/fields.py:516
+#: forms/fields.py:529
#, python-format
msgid "Ensure this filename has at most %(max)d character (it has %(length)d)."
msgid_plural ""
@@ -677,22 +685,22 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: forms/fields.py:519
+#: forms/fields.py:532
msgid "Please either submit a file or check the clear checkbox, not both."
msgstr ""
-#: forms/fields.py:580
+#: forms/fields.py:593
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr ""
-#: forms/fields.py:733 forms/fields.py:811
+#: forms/fields.py:746 forms/fields.py:824 forms/models.py:1042
#, python-format
msgid "Select a valid choice. %(value)s is not one of the available choices."
msgstr ""
-#: forms/fields.py:812 forms/fields.py:915 forms/models.py:1026
+#: forms/fields.py:825 forms/fields.py:928 forms/models.py:1041
msgid "Enter a list of values."
msgstr ""
@@ -714,43 +722,38 @@ msgstr ""
msgid "Delete"
msgstr ""
-#: forms/models.py:584
+#: forms/models.py:599
#, python-format
msgid "Please correct the duplicate data for %(field)s."
msgstr ""
-#: forms/models.py:588
+#: forms/models.py:603
#, python-format
msgid "Please correct the duplicate data for %(field)s, which must be unique."
msgstr ""
-#: forms/models.py:594
+#: forms/models.py:609
#, python-format
msgid ""
"Please correct the duplicate data for %(field_name)s which must be unique "
"for the %(lookup)s in %(date_field)s."
msgstr ""
-#: forms/models.py:602
+#: forms/models.py:617
msgid "Please correct the duplicate values below."
msgstr ""
-#: forms/models.py:868
+#: forms/models.py:883
msgid "The inline foreign key did not match the parent instance primary key."
msgstr ""
-#: forms/models.py:932
+#: forms/models.py:947
msgid "Select a valid choice. That choice is not one of the available choices."
msgstr ""
-#: forms/models.py:1027
+#: forms/models.py:1044
#, python-format
-msgid "Select a valid choice. %s is not one of the available choices."
-msgstr ""
-
-#: forms/models.py:1029
-#, python-format
-msgid "\"%s\" is not a valid value for a primary key."
+msgid "\"%(pk)s\" is not a valid value for a primary key."
msgstr ""
#: forms/util.py:84
@@ -760,27 +763,27 @@ msgid ""
"may be ambiguous or it may not exist."
msgstr ""
-#: forms/widgets.py:326
+#: forms/widgets.py:327
msgid "Currently"
msgstr ""
-#: forms/widgets.py:327
+#: forms/widgets.py:328
msgid "Change"
msgstr ""
-#: forms/widgets.py:328
+#: forms/widgets.py:329
msgid "Clear"
msgstr ""
-#: forms/widgets.py:546
+#: forms/widgets.py:547
msgid "Unknown"
msgstr ""
-#: forms/widgets.py:547
+#: forms/widgets.py:548
msgid "Yes"
msgstr ""
-#: forms/widgets.py:548
+#: forms/widgets.py:549
msgid "No"
msgstr ""
diff --git a/django/contrib/admin/locale/en/LC_MESSAGES/django.mo b/django/contrib/admin/locale/en/LC_MESSAGES/django.mo
index 7b60cfe7a9..08a7b68596 100644
Binary files a/django/contrib/admin/locale/en/LC_MESSAGES/django.mo and b/django/contrib/admin/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/admin/locale/en/LC_MESSAGES/django.po b/django/contrib/admin/locale/en/LC_MESSAGES/django.po
index 53f64941f4..964b0a509b 100644
--- a/django/contrib/admin/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/admin/locale/en/LC_MESSAGES/django.po
@@ -1,75 +1,72 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2013-01-02 08:52+0000\n"
-"Last-Translator: Ramiro Morales \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: actions.py:48
+#: actions.py:49
#, python-format
msgid "Successfully deleted %(count)d %(items)s."
-msgstr "Successfully deleted %(count)d %(items)s."
+msgstr ""
-#: actions.py:60 options.py:1361
+#: actions.py:61 options.py:1365
#, python-format
msgid "Cannot delete %(name)s"
-msgstr "Cannot delete %(name)s"
+msgstr ""
-#: actions.py:62 options.py:1363
+#: actions.py:63 options.py:1367
msgid "Are you sure?"
-msgstr "Are you sure?"
+msgstr ""
-#: actions.py:83
+#: actions.py:84
#, python-format
msgid "Delete selected %(verbose_name_plural)s"
-msgstr "Delete selected %(verbose_name_plural)s"
+msgstr ""
#: filters.py:101 filters.py:197 filters.py:237 filters.py:274 filters.py:380
msgid "All"
-msgstr "All"
+msgstr ""
#: filters.py:238
msgid "Yes"
-msgstr "Yes"
+msgstr ""
#: filters.py:239
msgid "No"
-msgstr "No"
+msgstr ""
#: filters.py:253
msgid "Unknown"
-msgstr "Unknown"
+msgstr ""
#: filters.py:308
msgid "Any date"
-msgstr "Any date"
+msgstr ""
#: filters.py:309
msgid "Today"
-msgstr "Today"
+msgstr ""
#: filters.py:313
msgid "Past 7 days"
-msgstr "Past 7 days"
+msgstr ""
#: filters.py:317
msgid "This month"
-msgstr "This month"
+msgstr ""
#: filters.py:321
msgid "This year"
-msgstr "This year"
+msgstr ""
#: forms.py:9
#, python-format
@@ -77,102 +74,99 @@ msgid ""
"Please enter the correct %(username)s and password for a staff account. Note "
"that both fields may be case-sensitive."
msgstr ""
-"Please enter the correct %(username)s and password for a staff account. Note "
-"that both fields may be case-sensitive."
#: forms.py:19
msgid "Please log in again, because your session has expired."
-msgstr "Please log in again, because your session has expired."
+msgstr ""
#: helpers.py:23
msgid "Action:"
-msgstr "Action:"
+msgstr ""
#: models.py:25
msgid "action time"
-msgstr "action time"
+msgstr ""
#: models.py:28
msgid "object id"
-msgstr "object id"
+msgstr ""
#: models.py:29
msgid "object repr"
-msgstr "object repr"
+msgstr ""
#: models.py:30
msgid "action flag"
-msgstr "action flag"
+msgstr ""
#: models.py:31
msgid "change message"
-msgstr "change message"
+msgstr ""
#: models.py:36
msgid "log entry"
-msgstr "log entry"
+msgstr ""
#: models.py:37
msgid "log entries"
-msgstr "log entries"
+msgstr ""
#: models.py:46
#, python-format
msgid "Added \"%(object)s\"."
-msgstr "Added \"%(object)s\"."
+msgstr ""
#: models.py:48
#, python-format
msgid "Changed \"%(object)s\" - %(changes)s"
-msgstr "Changed \"%(object)s\" - %(changes)s"
+msgstr ""
#: models.py:53
#, python-format
msgid "Deleted \"%(object)s.\""
-msgstr "Deleted \"%(object)s.\""
+msgstr ""
#: models.py:55
msgid "LogEntry Object"
-msgstr "LogEntry Object"
+msgstr ""
#: options.py:163 options.py:192
msgid "None"
-msgstr "None"
+msgstr ""
#: options.py:710
#, python-format
msgid "Changed %s."
-msgstr "Changed %s."
+msgstr ""
-#: options.py:710 options.py:720 options.py:1510
+#: options.py:710 options.py:720 options.py:1514
msgid "and"
-msgstr "and"
+msgstr ""
#: options.py:715
#, python-format
msgid "Added %(name)s \"%(object)s\"."
-msgstr "Added %(name)s \"%(object)s\"."
+msgstr ""
#: options.py:719
#, python-format
msgid "Changed %(list)s for %(name)s \"%(object)s\"."
-msgstr "Changed %(list)s for %(name)s \"%(object)s\"."
+msgstr ""
#: options.py:724
#, python-format
msgid "Deleted %(name)s \"%(object)s\"."
-msgstr "Deleted %(name)s \"%(object)s\"."
+msgstr ""
#: options.py:728
msgid "No fields changed."
-msgstr "No fields changed."
+msgstr ""
#: options.py:831 options.py:874
#, python-format
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr ""
-"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
#: options.py:849
#, python-format
@@ -180,13 +174,11 @@ msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may add another "
"%(name)s below."
msgstr ""
-"The %(name)s \"%(obj)s\" was added successfully. You may add another "
-"%(name)s below."
#: options.py:853
#, python-format
msgid "The %(name)s \"%(obj)s\" was added successfully."
-msgstr "The %(name)s \"%(obj)s\" was added successfully."
+msgstr ""
#: options.py:867
#, python-format
@@ -194,8 +186,6 @@ msgid ""
"The %(name)s \"%(obj)s\" was changed successfully. You may edit it again "
"below."
msgstr ""
-"The %(name)s \"%(obj)s\" was changed successfully. You may edit it again "
-"below."
#: options.py:881
#, python-format
@@ -203,135 +193,129 @@ msgid ""
"The %(name)s \"%(obj)s\" was changed successfully. You may add another "
"%(name)s below."
msgstr ""
-"The %(name)s \"%(obj)s\" was changed successfully. You may add another "
-"%(name)s below."
#: options.py:887
#, python-format
msgid "The %(name)s \"%(obj)s\" was changed successfully."
-msgstr "The %(name)s \"%(obj)s\" was changed successfully."
+msgstr ""
#: options.py:965 options.py:1225
msgid ""
"Items must be selected in order to perform actions on them. No items have "
"been changed."
msgstr ""
-"Items must be selected in order to perform actions on them. No items have "
-"been changed."
#: options.py:984
msgid "No action selected."
-msgstr "No action selected."
+msgstr ""
#: options.py:1064
#, python-format
msgid "Add %s"
-msgstr "Add %s"
+msgstr ""
#: options.py:1088 options.py:1333
#, python-format
msgid "%(name)s object with primary key %(key)r does not exist."
-msgstr "%(name)s object with primary key %(key)r does not exist."
+msgstr ""
#: options.py:1154
#, python-format
msgid "Change %s"
-msgstr "Change %s"
+msgstr ""
#: options.py:1204
msgid "Database error"
-msgstr "Database error"
+msgstr ""
#: options.py:1267
#, python-format
msgid "%(count)s %(name)s was changed successfully."
msgid_plural "%(count)s %(name)s were changed successfully."
-msgstr[0] "%(count)s %(name)s was changed successfully."
-msgstr[1] "%(count)s %(name)s were changed successfully."
+msgstr[0] ""
+msgstr[1] ""
#: options.py:1294
#, python-format
msgid "%(total_count)s selected"
msgid_plural "All %(total_count)s selected"
-msgstr[0] "%(total_count)s selected"
-msgstr[1] "All %(total_count)s selected"
+msgstr[0] ""
+msgstr[1] ""
#: options.py:1299
#, python-format
msgid "0 of %(cnt)s selected"
-msgstr "0 of %(cnt)s selected"
+msgstr ""
-#: options.py:1349
+#: options.py:1350
#, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
-msgstr "The %(name)s \"%(obj)s\" was deleted successfully."
+msgstr ""
-#: options.py:1402
+#: options.py:1406
#, python-format
msgid "Change history: %s"
-msgstr "Change history: %s"
+msgstr ""
#. Translators: Model verbose name and instance representation, suitable to be an item in a list
-#: options.py:1504
+#: options.py:1508
#, python-format
msgid "%(class_name)s %(instance)s"
msgstr ""
-#: options.py:1511
-#, fuzzy, python-format
+#: options.py:1515
+#, python-format
msgid ""
"Deleting %(class_name)s %(instance)s would require deleting the following "
"protected related objects: %(related_objects)s"
msgstr ""
-"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the "
-"following protected related objects:"
#: sites.py:324 tests.py:71 templates/admin/login.html:48
#: templates/registration/password_reset_complete.html:19
#: views/decorators.py:24
msgid "Log in"
-msgstr "Log in"
+msgstr ""
#: sites.py:392
msgid "Site administration"
-msgstr "Site administration"
+msgstr ""
#: sites.py:446
#, python-format
msgid "%s administration"
-msgstr "%s administration"
+msgstr ""
#: widgets.py:90
msgid "Date:"
-msgstr "Date:"
+msgstr ""
#: widgets.py:91
msgid "Time:"
-msgstr "Time:"
+msgstr ""
#: widgets.py:165
msgid "Lookup"
-msgstr "Lookup"
+msgstr ""
#: widgets.py:260
msgid "Add Another"
-msgstr "Add Another"
+msgstr ""
#: widgets.py:302
msgid "Currently:"
-msgstr "Currently:"
+msgstr ""
#: widgets.py:303
msgid "Change:"
-msgstr "Change:"
+msgstr ""
#: templates/admin/404.html:4 templates/admin/404.html.py:8
msgid "Page not found"
-msgstr "Page not found"
+msgstr ""
#: templates/admin/404.html:10
msgid "We're sorry, but the requested page could not be found."
-msgstr "We're sorry, but the requested page could not be found."
+msgstr ""
#: templates/admin/500.html:6 templates/admin/app_index.html:7
#: templates/admin/base.html:47 templates/admin/change_form.html:19
@@ -348,63 +332,61 @@ msgstr "We're sorry, but the requested page could not be found."
#: templates/registration/password_reset_done.html:6
#: templates/registration/password_reset_form.html:6
msgid "Home"
-msgstr "Home"
+msgstr ""
#: templates/admin/500.html:7
msgid "Server error"
-msgstr "Server error"
+msgstr ""
#: templates/admin/500.html:11
msgid "Server error (500)"
-msgstr "Server error (500)"
+msgstr ""
#: templates/admin/500.html:14
msgid "Server Error (500)"
-msgstr "Server Error (500)"
+msgstr ""
#: templates/admin/500.html:15
msgid ""
"There's been an error. It's been reported to the site administrators via "
"email and should be fixed shortly. Thanks for your patience."
msgstr ""
-"There's been an error. It's been reported to the site administrators via "
-"email and should be fixed shortly. Thanks for your patience."
#: templates/admin/actions.html:4
msgid "Run the selected action"
-msgstr "Run the selected action"
+msgstr ""
#: templates/admin/actions.html:4
msgid "Go"
-msgstr "Go"
+msgstr ""
#: templates/admin/actions.html:11
msgid "Click here to select the objects across all pages"
-msgstr "Click here to select the objects across all pages"
+msgstr ""
#: templates/admin/actions.html:11
#, python-format
msgid "Select all %(total_count)s %(module_name)s"
-msgstr "Select all %(total_count)s %(module_name)s"
+msgstr ""
#: templates/admin/actions.html:13
msgid "Clear selection"
-msgstr "Clear selection"
+msgstr ""
#: templates/admin/app_index.html:10 templates/admin/index.html:21
#, python-format
msgid "%(name)s"
-msgstr "%(name)s"
+msgstr ""
#: templates/admin/base.html:28
msgid "Welcome,"
-msgstr "Welcome,"
+msgstr ""
#: templates/admin/base.html:33
#: templates/registration/password_change_done.html:3
#: templates/registration/password_change_form.html:4
msgid "Documentation"
-msgstr "Documentation"
+msgstr ""
#: templates/admin/base.html:36
#: templates/admin/auth/user/change_password.html:17
@@ -412,35 +394,35 @@ msgstr "Documentation"
#: templates/registration/password_change_done.html:3
#: templates/registration/password_change_form.html:4
msgid "Change password"
-msgstr "Change password"
+msgstr ""
#: templates/admin/base.html:38
#: templates/registration/password_change_done.html:3
#: templates/registration/password_change_form.html:4
msgid "Log out"
-msgstr "Log out"
+msgstr ""
#: templates/admin/base_site.html:4
msgid "Django site admin"
-msgstr "Django site admin"
+msgstr ""
#: templates/admin/base_site.html:7
msgid "Django administration"
-msgstr "Django administration"
+msgstr ""
#: templates/admin/change_form.html:22 templates/admin/index.html:33
msgid "Add"
-msgstr "Add"
+msgstr ""
#: templates/admin/change_form.html:32 templates/admin/object_history.html:11
msgid "History"
-msgstr "History"
+msgstr ""
#: templates/admin/change_form.html:33
#: templates/admin/edit_inline/stacked.html:9
#: templates/admin/edit_inline/tabular.html:30
msgid "View on site"
-msgstr "View on site"
+msgstr ""
#: templates/admin/change_form.html:44 templates/admin/change_list.html:67
#: templates/admin/login.html:17
@@ -448,35 +430,35 @@ msgstr "View on site"
#: templates/registration/password_change_form.html:20
msgid "Please correct the error below."
msgid_plural "Please correct the errors below."
-msgstr[0] "Please correct the error below."
-msgstr[1] "Please correct the errors below."
+msgstr[0] ""
+msgstr[1] ""
#: templates/admin/change_list.html:58
#, python-format
msgid "Add %(name)s"
-msgstr "Add %(name)s"
+msgstr ""
#: templates/admin/change_list.html:78
msgid "Filter"
-msgstr "Filter"
+msgstr ""
#: templates/admin/change_list_results.html:17
msgid "Remove from sorting"
-msgstr "Remove from sorting"
+msgstr ""
#: templates/admin/change_list_results.html:18
#, python-format
msgid "Sorting priority: %(priority_number)s"
-msgstr "Sorting priority: %(priority_number)s"
+msgstr ""
#: templates/admin/change_list_results.html:19
msgid "Toggle sorting"
-msgstr "Toggle sorting"
+msgstr ""
#: templates/admin/delete_confirmation.html:11
#: templates/admin/submit_line.html:4
msgid "Delete"
-msgstr "Delete"
+msgstr ""
#: templates/admin/delete_confirmation.html:18
#, python-format
@@ -485,9 +467,6 @@ msgid ""
"related objects, but your account doesn't have permission to delete the "
"following types of objects:"
msgstr ""
-"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting "
-"related objects, but your account doesn't have permission to delete the "
-"following types of objects:"
#: templates/admin/delete_confirmation.html:26
#, python-format
@@ -495,8 +474,6 @@ msgid ""
"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the "
"following protected related objects:"
msgstr ""
-"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the "
-"following protected related objects:"
#: templates/admin/delete_confirmation.html:34
#, python-format
@@ -504,17 +481,15 @@ msgid ""
"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? "
"All of the following related items will be deleted:"
msgstr ""
-"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? "
-"All of the following related items will be deleted:"
#: templates/admin/delete_confirmation.html:39
#: templates/admin/delete_selected_confirmation.html:44
msgid "Yes, I'm sure"
-msgstr "Yes, I'm sure"
+msgstr ""
#: templates/admin/delete_selected_confirmation.html:10
msgid "Delete multiple objects"
-msgstr "Delete multiple objects"
+msgstr ""
#: templates/admin/delete_selected_confirmation.html:17
#, python-format
@@ -523,9 +498,6 @@ msgid ""
"objects, but your account doesn't have permission to delete the following "
"types of objects:"
msgstr ""
-"Deleting the selected %(objects_name)s would result in deleting related "
-"objects, but your account doesn't have permission to delete the following "
-"types of objects:"
#: templates/admin/delete_selected_confirmation.html:25
#, python-format
@@ -533,8 +505,6 @@ msgid ""
"Deleting the selected %(objects_name)s would require deleting the following "
"protected related objects:"
msgstr ""
-"Deleting the selected %(objects_name)s would require deleting the following "
-"protected related objects:"
#: templates/admin/delete_selected_confirmation.html:33
#, python-format
@@ -542,42 +512,40 @@ msgid ""
"Are you sure you want to delete the selected %(objects_name)s? All of the "
"following objects and their related items will be deleted:"
msgstr ""
-"Are you sure you want to delete the selected %(objects_name)s? All of the "
-"following objects and their related items will be deleted:"
#: templates/admin/filter.html:2
#, python-format
msgid " By %(filter_title)s "
-msgstr " By %(filter_title)s "
+msgstr ""
#: templates/admin/index.html:20
#, python-format
msgid "Models in the %(name)s application"
-msgstr "Models in the %(name)s application"
+msgstr ""
#: templates/admin/index.html:39
msgid "Change"
-msgstr "Change"
+msgstr ""
#: templates/admin/index.html:49
msgid "You don't have permission to edit anything."
-msgstr "You don't have permission to edit anything."
+msgstr ""
#: templates/admin/index.html:57
msgid "Recent Actions"
-msgstr "Recent Actions"
+msgstr ""
#: templates/admin/index.html:58
msgid "My Actions"
-msgstr "My Actions"
+msgstr ""
#: templates/admin/index.html:62
msgid "None available"
-msgstr "None available"
+msgstr ""
#: templates/admin/index.html:76
msgid "Unknown content"
-msgstr "Unknown content"
+msgstr ""
#: templates/admin/invalid_setup.html:12
msgid ""
@@ -585,163 +553,154 @@ msgid ""
"database tables have been created, and make sure the database is readable by "
"the appropriate user."
msgstr ""
-"Something's wrong with your database installation. Make sure the appropriate "
-"database tables have been created, and make sure the database is readable by "
-"the appropriate user."
#: templates/admin/login.html:37
msgid "Password:"
-msgstr "Password:"
+msgstr ""
#: templates/admin/login.html:44
msgid "Forgotten your password or username?"
-msgstr "Forgotten your password or username?"
+msgstr ""
#: templates/admin/object_history.html:23
msgid "Date/time"
-msgstr "Date/time"
+msgstr ""
#: templates/admin/object_history.html:24
msgid "User"
-msgstr "User"
+msgstr ""
#: templates/admin/object_history.html:25
msgid "Action"
-msgstr "Action"
+msgstr ""
#: templates/admin/object_history.html:39
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
-"This object doesn't have a change history. It probably wasn't added via this "
-"admin site."
#: templates/admin/pagination.html:10
msgid "Show all"
-msgstr "Show all"
+msgstr ""
#: templates/admin/pagination.html:11 templates/admin/submit_line.html:3
msgid "Save"
-msgstr "Save"
+msgstr ""
#: templates/admin/search_form.html:7
msgid "Search"
-msgstr "Search"
+msgstr ""
#: templates/admin/search_form.html:9
#, python-format
msgid "%(counter)s result"
msgid_plural "%(counter)s results"
-msgstr[0] "%(counter)s result"
-msgstr[1] "%(counter)s results"
+msgstr[0] ""
+msgstr[1] ""
#: templates/admin/search_form.html:9
#, python-format
msgid "%(full_result_count)s total"
-msgstr "%(full_result_count)s total"
+msgstr ""
#: templates/admin/submit_line.html:5
msgid "Save as new"
-msgstr "Save as new"
+msgstr ""
#: templates/admin/submit_line.html:6
msgid "Save and add another"
-msgstr "Save and add another"
+msgstr ""
#: templates/admin/submit_line.html:7
msgid "Save and continue editing"
-msgstr "Save and continue editing"
+msgstr ""
#: templates/admin/auth/user/add_form.html:6
msgid ""
"First, enter a username and password. Then, you'll be able to edit more user "
"options."
msgstr ""
-"First, enter a username and password. Then, you'll be able to edit more user "
-"options."
#: templates/admin/auth/user/add_form.html:8
msgid "Enter a username and password."
-msgstr "Enter a username and password."
+msgstr ""
#: templates/admin/auth/user/change_password.html:31
#, python-format
msgid "Enter a new password for the user %(username)s."
-msgstr "Enter a new password for the user %(username)s."
+msgstr ""
#: templates/admin/auth/user/change_password.html:38
msgid "Password"
-msgstr "Password"
+msgstr ""
#: templates/admin/auth/user/change_password.html:44
#: templates/registration/password_change_form.html:42
msgid "Password (again)"
-msgstr "Password (again)"
+msgstr ""
#: templates/admin/auth/user/change_password.html:45
msgid "Enter the same password as above, for verification."
-msgstr "Enter the same password as above, for verification."
+msgstr ""
#: templates/admin/edit_inline/stacked.html:26
#: templates/admin/edit_inline/tabular.html:76
msgid "Remove"
-msgstr "Remove"
+msgstr ""
#: templates/admin/edit_inline/stacked.html:27
#: templates/admin/edit_inline/tabular.html:75
#, python-format
msgid "Add another %(verbose_name)s"
-msgstr "Add another %(verbose_name)s"
+msgstr ""
#: templates/admin/edit_inline/tabular.html:17
msgid "Delete?"
-msgstr "Delete?"
+msgstr ""
#: templates/registration/logged_out.html:8
msgid "Thanks for spending some quality time with the Web site today."
-msgstr "Thanks for spending some quality time with the Web site today."
+msgstr ""
#: templates/registration/logged_out.html:10
msgid "Log in again"
-msgstr "Log in again"
+msgstr ""
#: templates/registration/password_change_done.html:7
#: templates/registration/password_change_form.html:8
#: templates/registration/password_change_form.html:12
#: templates/registration/password_change_form.html:24
msgid "Password change"
-msgstr "Password change"
+msgstr ""
#: templates/registration/password_change_done.html:11
#: templates/registration/password_change_done.html:15
msgid "Password change successful"
-msgstr "Password change successful"
+msgstr ""
#: templates/registration/password_change_done.html:17
msgid "Your password was changed."
-msgstr "Your password was changed."
+msgstr ""
#: templates/registration/password_change_form.html:26
msgid ""
"Please enter your old password, for security's sake, and then enter your new "
"password twice so we can verify you typed it in correctly."
msgstr ""
-"Please enter your old password, for security's sake, and then enter your new "
-"password twice so we can verify you typed it in correctly."
#: templates/registration/password_change_form.html:32
msgid "Old password"
-msgstr "Old password"
+msgstr ""
#: templates/registration/password_change_form.html:37
msgid "New password"
-msgstr "New password"
+msgstr ""
#: templates/registration/password_change_form.html:48
#: templates/registration/password_reset_confirm.html:26
msgid "Change my password"
-msgstr "Change my password"
+msgstr ""
#: templates/registration/password_reset_complete.html:7
#: templates/registration/password_reset_confirm.html:11
@@ -750,66 +709,59 @@ msgstr "Change my password"
#: templates/registration/password_reset_form.html:11
#: templates/registration/password_reset_form.html:15
msgid "Password reset"
-msgstr "Password reset"
+msgstr ""
#: templates/registration/password_reset_complete.html:11
#: templates/registration/password_reset_complete.html:15
msgid "Password reset complete"
-msgstr "Password reset complete"
+msgstr ""
#: templates/registration/password_reset_complete.html:17
msgid "Your password has been set. You may go ahead and log in now."
-msgstr "Your password has been set. You may go ahead and log in now."
+msgstr ""
#: templates/registration/password_reset_confirm.html:7
msgid "Password reset confirmation"
-msgstr "Password reset confirmation"
+msgstr ""
#: templates/registration/password_reset_confirm.html:17
msgid "Enter new password"
-msgstr "Enter new password"
+msgstr ""
#: templates/registration/password_reset_confirm.html:19
msgid ""
"Please enter your new password twice so we can verify you typed it in "
"correctly."
msgstr ""
-"Please enter your new password twice so we can verify you typed it in "
-"correctly."
#: templates/registration/password_reset_confirm.html:23
msgid "New password:"
-msgstr "New password:"
+msgstr ""
#: templates/registration/password_reset_confirm.html:25
msgid "Confirm password:"
-msgstr "Confirm password:"
+msgstr ""
#: templates/registration/password_reset_confirm.html:31
msgid "Password reset unsuccessful"
-msgstr "Password reset unsuccessful"
+msgstr ""
#: templates/registration/password_reset_confirm.html:33
msgid ""
"The password reset link was invalid, possibly because it has already been "
"used. Please request a new password reset."
msgstr ""
-"The password reset link was invalid, possibly because it has already been "
-"used. Please request a new password reset."
#: templates/registration/password_reset_done.html:11
#: templates/registration/password_reset_done.html:15
msgid "Password reset successful"
-msgstr "Password reset successful"
+msgstr ""
#: templates/registration/password_reset_done.html:17
-#, fuzzy
msgid ""
"We've emailed you instructions for setting your password. You should be "
"receiving them shortly."
msgstr ""
-"We've emailed you instructions for setting your password to the email "
-"address you submitted. You should be receiving it shortly."
#: templates/registration/password_reset_done.html:19
msgid ""
@@ -823,56 +775,52 @@ msgid ""
"You're receiving this email because you requested a password reset for your "
"user account at %(site_name)s."
msgstr ""
-"You're receiving this email because you requested a password reset for your "
-"user account at %(site_name)s."
#: templates/registration/password_reset_email.html:4
msgid "Please go to the following page and choose a new password:"
-msgstr "Please go to the following page and choose a new password:"
+msgstr ""
#: templates/registration/password_reset_email.html:8
msgid "Your username, in case you've forgotten:"
-msgstr "Your username, in case you've forgotten:"
+msgstr ""
#: templates/registration/password_reset_email.html:10
msgid "Thanks for using our site!"
-msgstr "Thanks for using our site!"
+msgstr ""
#: templates/registration/password_reset_email.html:12
#, python-format
msgid "The %(site_name)s team"
-msgstr "The %(site_name)s team"
+msgstr ""
#: templates/registration/password_reset_form.html:17
msgid ""
"Forgotten your password? Enter your email address below, and we'll email "
"instructions for setting a new one."
msgstr ""
-"Forgotten your password? Enter your email address below, and we'll email "
-"instructions for setting a new one."
#: templates/registration/password_reset_form.html:21
msgid "Email address:"
-msgstr "Email address:"
+msgstr ""
#: templates/registration/password_reset_form.html:21
msgid "Reset my password"
-msgstr "Reset my password"
+msgstr ""
#: templatetags/admin_list.py:348
msgid "All dates"
-msgstr "All dates"
+msgstr ""
#: views/main.py:37
msgid "(None)"
-msgstr "(None)"
+msgstr ""
#: views/main.py:86
#, python-format
msgid "Select %s"
-msgstr "Select %s"
+msgstr ""
#: views/main.py:88
#, python-format
msgid "Select %s to change"
-msgstr "Select %s to change"
+msgstr ""
diff --git a/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.mo b/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.mo
index 9851b29c10..08a7b68596 100644
Binary files a/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.mo and b/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.mo differ
diff --git a/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.po b/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.po
index 64f3850b13..c913eee09b 100644
--- a/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.po
+++ b/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.po
@@ -1,25 +1,22 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-03-08 10:42+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/admin/js/SelectFilter2.js:45
#, c-format
msgid "Available %s"
-msgstr "Available %s"
+msgstr ""
#: static/admin/js/SelectFilter2.js:46
#, c-format
@@ -27,39 +24,37 @@ msgid ""
"This is the list of available %s. You may choose some by selecting them in "
"the box below and then clicking the \"Choose\" arrow between the two boxes."
msgstr ""
-"This is the list of available %s. You may choose some by selecting them in "
-"the box below and then clicking the \"Choose\" arrow between the two boxes."
#: static/admin/js/SelectFilter2.js:53
#, c-format
msgid "Type into this box to filter down the list of available %s."
-msgstr "Type into this box to filter down the list of available %s."
+msgstr ""
#: static/admin/js/SelectFilter2.js:57
msgid "Filter"
-msgstr "Filter"
+msgstr ""
#: static/admin/js/SelectFilter2.js:61
msgid "Choose all"
-msgstr "Choose all"
+msgstr ""
#: static/admin/js/SelectFilter2.js:61
#, c-format
msgid "Click to choose all %s at once."
-msgstr "Click to choose all %s at once."
+msgstr ""
#: static/admin/js/SelectFilter2.js:67
msgid "Choose"
-msgstr "Choose"
+msgstr ""
#: static/admin/js/SelectFilter2.js:69
msgid "Remove"
-msgstr "Remove"
+msgstr ""
#: static/admin/js/SelectFilter2.js:75
#, c-format
msgid "Chosen %s"
-msgstr "Chosen %s"
+msgstr ""
#: static/admin/js/SelectFilter2.js:76
#, c-format
@@ -67,31 +62,27 @@ msgid ""
"This is the list of chosen %s. You may remove some by selecting them in the "
"box below and then clicking the \"Remove\" arrow between the two boxes."
msgstr ""
-"This is the list of chosen %s. You may remove some by selecting them in the "
-"box below and then clicking the \"Remove\" arrow between the two boxes."
#: static/admin/js/SelectFilter2.js:80
msgid "Remove all"
-msgstr "Remove all"
+msgstr ""
#: static/admin/js/SelectFilter2.js:80
#, c-format
msgid "Click to remove all chosen %s at once."
-msgstr "Click to remove all chosen %s at once."
+msgstr ""
#: static/admin/js/actions.js:18 static/admin/js/actions.min.js:1
msgid "%(sel)s of %(cnt)s selected"
msgid_plural "%(sel)s of %(cnt)s selected"
-msgstr[0] "%(sel)s of %(cnt)s selected"
-msgstr[1] "%(sel)s of %(cnt)s selected"
+msgstr[0] ""
+msgstr[1] ""
#: static/admin/js/actions.js:109 static/admin/js/actions.min.js:5
msgid ""
"You have unsaved changes on individual editable fields. If you run an "
"action, your unsaved changes will be lost."
msgstr ""
-"You have unsaved changes on individual editable fields. If you run an "
-"action, your unsaved changes will be lost."
#: static/admin/js/actions.js:121 static/admin/js/actions.min.js:5
msgid ""
@@ -99,9 +90,6 @@ msgid ""
"individual fields yet. Please click OK to save. You'll need to re-run the "
"action."
msgstr ""
-"You have selected an action, but you haven't saved your changes to "
-"individual fields yet. Please click OK to save. You'll need to re-run the "
-"action."
#: static/admin/js/actions.js:123 static/admin/js/actions.min.js:6
msgid ""
@@ -109,74 +97,69 @@ msgid ""
"fields. You're probably looking for the Go button rather than the Save "
"button."
msgstr ""
-"You have selected an action, and you haven't made any changes on individual "
-"fields. You're probably looking for the Go button rather than the Save "
-"button."
#: static/admin/js/calendar.js:8
msgid ""
"January February March April May June July August September October November "
"December"
msgstr ""
-"January February March April May June July August September October November "
-"December"
#: static/admin/js/calendar.js:9
msgid "S M T W T F S"
-msgstr "S M T W T F S"
+msgstr ""
#: static/admin/js/collapse.js:8 static/admin/js/collapse.js.c:19
#: static/admin/js/collapse.min.js:1
msgid "Show"
-msgstr "Show"
+msgstr ""
#: static/admin/js/collapse.js:16 static/admin/js/collapse.min.js:1
msgid "Hide"
-msgstr "Hide"
-
-#: static/admin/js/admin/DateTimeShortcuts.js:49
-#: static/admin/js/admin/DateTimeShortcuts.js:85
-msgid "Now"
-msgstr "Now"
-
-#: static/admin/js/admin/DateTimeShortcuts.js:53
-msgid "Clock"
-msgstr "Clock"
-
-#: static/admin/js/admin/DateTimeShortcuts.js:81
-msgid "Choose a time"
-msgstr "Choose a time"
-
-#: static/admin/js/admin/DateTimeShortcuts.js:86
-msgid "Midnight"
-msgstr "Midnight"
-
-#: static/admin/js/admin/DateTimeShortcuts.js:87
-msgid "6 a.m."
-msgstr "6 a.m."
+msgstr ""
+#: static/admin/js/admin/DateTimeShortcuts.js:52
#: static/admin/js/admin/DateTimeShortcuts.js:88
+msgid "Now"
+msgstr ""
+
+#: static/admin/js/admin/DateTimeShortcuts.js:56
+msgid "Clock"
+msgstr ""
+
+#: static/admin/js/admin/DateTimeShortcuts.js:84
+msgid "Choose a time"
+msgstr ""
+
+#: static/admin/js/admin/DateTimeShortcuts.js:89
+msgid "Midnight"
+msgstr ""
+
+#: static/admin/js/admin/DateTimeShortcuts.js:90
+msgid "6 a.m."
+msgstr ""
+
+#: static/admin/js/admin/DateTimeShortcuts.js:91
msgid "Noon"
-msgstr "Noon"
+msgstr ""
-#: static/admin/js/admin/DateTimeShortcuts.js:92
-#: static/admin/js/admin/DateTimeShortcuts.js:204
+#: static/admin/js/admin/DateTimeShortcuts.js:95
+#: static/admin/js/admin/DateTimeShortcuts.js:208
msgid "Cancel"
-msgstr "Cancel"
-
-#: static/admin/js/admin/DateTimeShortcuts.js:144
-#: static/admin/js/admin/DateTimeShortcuts.js:197
-msgid "Today"
-msgstr "Today"
+msgstr ""
#: static/admin/js/admin/DateTimeShortcuts.js:148
-msgid "Calendar"
-msgstr "Calendar"
+#: static/admin/js/admin/DateTimeShortcuts.js:201
+msgid "Today"
+msgstr ""
-#: static/admin/js/admin/DateTimeShortcuts.js:195
-msgid "Yesterday"
-msgstr "Yesterday"
+#: static/admin/js/admin/DateTimeShortcuts.js:152
+msgid "Calendar"
+msgstr ""
#: static/admin/js/admin/DateTimeShortcuts.js:199
+msgid "Yesterday"
+msgstr ""
+
+#: static/admin/js/admin/DateTimeShortcuts.js:203
msgid "Tomorrow"
-msgstr "Tomorrow"
+msgstr ""
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index b35f100fda..9bc3d55454 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -5,7 +5,7 @@ from django import forms
from django.conf import settings
from django.forms.formsets import all_valid, DELETION_FIELD_NAME
from django.forms.models import (modelform_factory, modelformset_factory,
- inlineformset_factory, BaseInlineFormSet)
+ inlineformset_factory, BaseInlineFormSet, modelform_defines_fields)
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin import widgets, helpers
from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_objects,
@@ -13,7 +13,7 @@ from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_o
from django.contrib.admin.templatetags.admin_static import static
from django.contrib import messages
from django.views.decorators.csrf import csrf_protect
-from django.core.exceptions import PermissionDenied, ValidationError
+from django.core.exceptions import PermissionDenied, ValidationError, FieldError
from django.core.paginator import Paginator
from django.core.urlresolvers import reverse
from django.db import models, transaction, router
@@ -488,7 +488,15 @@ class ModelAdmin(BaseModelAdmin):
"formfield_callback": partial(self.formfield_for_dbfield, request=request),
}
defaults.update(kwargs)
- return modelform_factory(self.model, **defaults)
+
+ if defaults['fields'] is None and not modelform_defines_fields(defaults['form']):
+ defaults['fields'] = forms.ALL_FIELDS
+
+ try:
+ return modelform_factory(self.model, **defaults)
+ except FieldError as e:
+ raise FieldError('%s. Check fields/fieldsets/exclude attributes of class %s.'
+ % (e, self.__class__.__name__))
def get_changelist(self, request, **kwargs):
"""
@@ -519,6 +527,10 @@ class ModelAdmin(BaseModelAdmin):
"formfield_callback": partial(self.formfield_for_dbfield, request=request),
}
defaults.update(kwargs)
+ if (defaults.get('fields') is None
+ and not modelform_defines_fields(defaults.get('form'))):
+ defaults['fields'] = forms.ALL_FIELDS
+
return modelform_factory(self.model, **defaults)
def get_changelist_formset(self, request, **kwargs):
@@ -1523,6 +1535,10 @@ class InlineModelAdmin(BaseModelAdmin):
return result
defaults['form'] = DeleteProtectedModelForm
+
+ if defaults['fields'] is None and not modelform_defines_fields(defaults['form']):
+ defaults['fields'] = forms.ALL_FIELDS
+
return inlineformset_factory(self.parent_model, self.model, **defaults)
def get_fieldsets(self, request, obj=None):
diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py
index a02bb7a316..8d65f96cf1 100644
--- a/django/contrib/admin/validation.py
+++ b/django/contrib/admin/validation.py
@@ -246,7 +246,6 @@ def validate_fields_spec(cls, model, opts, flds, label):
# readonly_fields will handle the validation of such
# things.
continue
- check_formfield(cls, model, opts, label, field)
try:
f = opts.get_field(field)
except models.FieldDoesNotExist:
@@ -302,14 +301,6 @@ def validate_base(cls, model):
# exclude
if cls.exclude: # default value is None
check_isseq(cls, 'exclude', cls.exclude)
- for field in cls.exclude:
- check_formfield(cls, model, opts, 'exclude', field)
- try:
- f = opts.get_field(field)
- except models.FieldDoesNotExist:
- # If we can't find a field on the model that matches,
- # it could be an extra field on the form.
- continue
if len(cls.exclude) > len(set(cls.exclude)):
raise ImproperlyConfigured('There are duplicate field(s) in %s.exclude' % cls.__name__)
@@ -380,23 +371,6 @@ def get_field(cls, model, opts, label, field):
raise ImproperlyConfigured("'%s.%s' refers to field '%s' that is missing from model '%s.%s'."
% (cls.__name__, label, field, model._meta.app_label, model.__name__))
-def check_formfield(cls, model, opts, label, field):
- if getattr(cls.form, 'base_fields', None):
- try:
- cls.form.base_fields[field]
- except KeyError:
- raise ImproperlyConfigured("'%s.%s' refers to field '%s' that "
- "is missing from the form." % (cls.__name__, label, field))
- else:
- get_form_is_overridden = hasattr(cls, 'get_form') and cls.get_form != ModelAdmin.get_form
- if not get_form_is_overridden:
- fields = fields_for_model(model)
- try:
- fields[field]
- except KeyError:
- raise ImproperlyConfigured("'%s.%s' refers to field '%s' that "
- "is missing from the form." % (cls.__name__, label, field))
-
def fetch_attr(cls, model, opts, label, field):
try:
return opts.get_field(field)
diff --git a/django/contrib/admindocs/locale/en/LC_MESSAGES/django.mo b/django/contrib/admindocs/locale/en/LC_MESSAGES/django.mo
index 6c452c7392..08a7b68596 100644
Binary files a/django/contrib/admindocs/locale/en/LC_MESSAGES/django.mo and b/django/contrib/admindocs/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/admindocs/locale/en/LC_MESSAGES/django.po b/django/contrib/admindocs/locale/en/LC_MESSAGES/django.po
index c95904e30a..2e4dcdf13d 100644
--- a/django/contrib/admindocs/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/admindocs/locale/en/LC_MESSAGES/django.po
@@ -1,77 +1,74 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-10-22 08:46+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: views.py:59 views.py:61 views.py:63
msgid "tag:"
-msgstr "tag:"
+msgstr ""
#: views.py:94 views.py:96 views.py:98
msgid "filter:"
-msgstr "filter:"
+msgstr ""
#: views.py:157 views.py:159 views.py:161
msgid "view:"
-msgstr "view:"
+msgstr ""
#: views.py:189
#, python-format
msgid "App %r not found"
-msgstr "App %r not found"
+msgstr ""
#: views.py:196
#, python-format
msgid "Model %(model_name)r not found in app %(app_label)r"
-msgstr "Model %(model_name)r not found in app %(app_label)r"
+msgstr ""
#: views.py:208
#, python-format
msgid "the related `%(app_label)s.%(data_type)s` object"
-msgstr "the related `%(app_label)s.%(data_type)s` object"
+msgstr ""
#: views.py:208 views.py:227 views.py:232 views.py:246 views.py:260
#: views.py:265
msgid "model:"
-msgstr "model:"
+msgstr ""
#: views.py:223 views.py:255
#, python-format
msgid "related `%(app_label)s.%(object_name)s` objects"
-msgstr "related `%(app_label)s.%(object_name)s` objects"
+msgstr ""
#: views.py:227 views.py:260
#, python-format
msgid "all %s"
-msgstr "all %s"
+msgstr ""
#: views.py:232 views.py:265
#, python-format
msgid "number of %s"
-msgstr "number of %s"
+msgstr ""
#: views.py:270
#, python-format
msgid "Fields on %s objects"
-msgstr "Fields on %s objects"
+msgstr ""
#: views.py:362
#, python-format
msgid "%s does not appear to be a urlpattern object"
-msgstr "%s does not appear to be a urlpattern object"
+msgstr ""
#: templates/admin_doc/bookmarklets.html:6 templates/admin_doc/index.html:6
#: templates/admin_doc/missing_docutils.html:6
@@ -83,7 +80,7 @@ msgstr "%s does not appear to be a urlpattern object"
#: templates/admin_doc/view_detail.html:6
#: templates/admin_doc/view_index.html:7
msgid "Home"
-msgstr "Home"
+msgstr ""
#: templates/admin_doc/bookmarklets.html:7 templates/admin_doc/index.html:7
#: templates/admin_doc/missing_docutils.html:7
@@ -95,15 +92,15 @@ msgstr "Home"
#: templates/admin_doc/view_detail.html:7
#: templates/admin_doc/view_index.html:8
msgid "Documentation"
-msgstr "Documentation"
+msgstr ""
#: templates/admin_doc/bookmarklets.html:8
msgid "Bookmarklets"
-msgstr "Bookmarklets"
+msgstr ""
#: templates/admin_doc/bookmarklets.html:11
msgid "Documentation bookmarklets"
-msgstr "Documentation bookmarklets"
+msgstr ""
#: templates/admin_doc/bookmarklets.html:15
msgid ""
@@ -115,81 +112,70 @@ msgid ""
"as \"internal\" (talk to your system administrator if you aren't sure if\n"
"your computer is \"internal\").
\n"
msgstr ""
-"\n"
-"
To install bookmarklets, drag the link to your bookmarks\n"
-"toolbar, or right-click the link and add it to your bookmarks. Now you can\n"
-"select the bookmarklet from any page in the site. Note that some of these\n"
-"bookmarklets require you to be viewing the site from a computer designated\n"
-"as \"internal\" (talk to your system administrator if you aren't sure if\n"
-"your computer is \"internal\").
\n"
#: templates/admin_doc/bookmarklets.html:25
msgid "Documentation for this page"
-msgstr "Documentation for this page"
+msgstr ""
#: templates/admin_doc/bookmarklets.html:26
msgid ""
"Jumps you from any page to the documentation for the view that generates "
"that page."
msgstr ""
-"Jumps you from any page to the documentation for the view that generates "
-"that page."
#: templates/admin_doc/bookmarklets.html:28
msgid "Show object ID"
-msgstr "Show object ID"
+msgstr ""
#: templates/admin_doc/bookmarklets.html:29
msgid ""
"Shows the content-type and unique ID for pages that represent a single "
"object."
msgstr ""
-"Shows the content-type and unique ID for pages that represent a single "
-"object."
#: templates/admin_doc/bookmarklets.html:31
msgid "Edit this object (current window)"
-msgstr "Edit this object (current window)"
+msgstr ""
#: templates/admin_doc/bookmarklets.html:32
msgid "Jumps to the admin page for pages that represent a single object."
-msgstr "Jumps to the admin page for pages that represent a single object."
+msgstr ""
#: templates/admin_doc/bookmarklets.html:34
msgid "Edit this object (new window)"
-msgstr "Edit this object (new window)"
+msgstr ""
#: templates/admin_doc/bookmarklets.html:35
msgid "As above, but opens the admin page in a new window."
-msgstr "As above, but opens the admin page in a new window."
+msgstr ""
#: templates/admin_doc/model_detail.html:16
#: templates/admin_doc/model_index.html:10
msgid "Models"
-msgstr "Models"
+msgstr ""
#: templates/admin_doc/template_detail.html:8
msgid "Templates"
-msgstr "Templates"
+msgstr ""
#: templates/admin_doc/template_filter_index.html:9
msgid "Filters"
-msgstr "Filters"
+msgstr ""
#: templates/admin_doc/template_tag_index.html:9
msgid "Tags"
-msgstr "Tags"
+msgstr ""
#: templates/admin_doc/view_detail.html:8
#: templates/admin_doc/view_index.html:9
msgid "Views"
-msgstr "Views"
+msgstr ""
-#: tests/__init__.py:23
+#: tests/test_fields.py:29
msgid "Boolean (Either True or False)"
-msgstr "Boolean (Either True or False)"
+msgstr ""
-#: tests/__init__.py:33
+#: tests/test_fields.py:39
#, python-format
msgid "Field of type: %(field_type)s"
-msgstr "Field of type: %(field_type)s"
+msgstr ""
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
index 42abde2f19..e44e7a703e 100644
--- a/django/contrib/auth/forms.py
+++ b/django/contrib/auth/forms.py
@@ -130,6 +130,7 @@ class UserChangeForm(forms.ModelForm):
class Meta:
model = User
+ fields = '__all__'
def __init__(self, *args, **kwargs):
super(UserChangeForm, self).__init__(*args, **kwargs)
diff --git a/django/contrib/auth/locale/en/LC_MESSAGES/django.mo b/django/contrib/auth/locale/en/LC_MESSAGES/django.mo
index 91fc85a68f..08a7b68596 100644
Binary files a/django/contrib/auth/locale/en/LC_MESSAGES/django.mo and b/django/contrib/auth/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/auth/locale/en/LC_MESSAGES/django.po b/django/contrib/auth/locale/en/LC_MESSAGES/django.po
index c2e2cdaef7..36faba74bf 100644
--- a/django/contrib/auth/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/auth/locale/en/LC_MESSAGES/django.po
@@ -1,81 +1,79 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-12-16 08:51+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin.py:41
msgid "Personal info"
-msgstr "Personal info"
+msgstr ""
#: admin.py:42
msgid "Permissions"
-msgstr "Permissions"
+msgstr ""
#: admin.py:44
msgid "Important dates"
-msgstr "Important dates"
+msgstr ""
#: admin.py:132
msgid "Password changed successfully."
-msgstr "Password changed successfully."
+msgstr ""
#: admin.py:142
#, python-format
msgid "Change password: %s"
-msgstr "Change password: %s"
+msgstr ""
-#: forms.py:33 tests/forms.py:261 tests/forms.py:266 tests/forms.py:407
+#: forms.py:33 tests/test_forms.py:261 tests/test_forms.py:266
+#: tests/test_forms.py:407
msgid "No password set."
-msgstr "No password set."
+msgstr ""
-#: forms.py:39 tests/forms.py:271 tests/forms.py:277
+#: forms.py:39 tests/test_forms.py:271 tests/test_forms.py:277
msgid "Invalid password format or unknown hashing algorithm."
-msgstr "Invalid password format or unknown hashing algorithm."
+msgstr ""
#: forms.py:72
msgid "A user with that username already exists."
-msgstr "A user with that username already exists."
+msgstr ""
#: forms.py:73 forms.py:254 forms.py:314
msgid "The two password fields didn't match."
-msgstr "The two password fields didn't match."
+msgstr ""
#: forms.py:75 forms.py:120
msgid "Username"
-msgstr "Username"
+msgstr ""
#: forms.py:77 forms.py:121
msgid "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."
-msgstr "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."
+msgstr ""
#: forms.py:80 forms.py:124
msgid "This value may contain only letters, numbers and @/./+/-/_ characters."
-msgstr "This value may contain only letters, numbers and @/./+/-/_ characters."
+msgstr ""
#: forms.py:82 forms.py:126 forms.py:153 forms.py:316
msgid "Password"
-msgstr "Password"
+msgstr ""
#: forms.py:84
msgid "Password confirmation"
-msgstr "Password confirmation"
+msgstr ""
#: forms.py:86
msgid "Enter the same password as above, for verification."
-msgstr "Enter the same password as above, for verification."
+msgstr ""
#: forms.py:127
msgid ""
@@ -83,9 +81,6 @@ msgid ""
"password, but you can change the password using this "
"form."
msgstr ""
-"Raw passwords are not stored, so there is no way to see this user's "
-"password, but you can change the password using this "
-"form."
#: forms.py:156
#, python-format
@@ -93,208 +88,177 @@ msgid ""
"Please enter a correct %(username)s and password. Note that both fields may "
"be case-sensitive."
msgstr ""
-"Please enter a correct %(username)s and password. Note that both fields may "
-"be case-sensitive."
#: forms.py:158
msgid "This account is inactive."
-msgstr "This account is inactive."
+msgstr ""
#: forms.py:206
msgid "Email"
-msgstr "Email"
+msgstr ""
#: forms.py:256
msgid "New password"
-msgstr "New password"
+msgstr ""
#: forms.py:258
msgid "New password confirmation"
-msgstr "New password confirmation"
+msgstr ""
#: forms.py:287
msgid "Your old password was entered incorrectly. Please enter it again."
-msgstr "Your old password was entered incorrectly. Please enter it again."
+msgstr ""
#: forms.py:290
msgid "Old password"
-msgstr "Old password"
+msgstr ""
#: forms.py:318
msgid "Password (again)"
-msgstr "Password (again)"
+msgstr ""
#: hashers.py:243 hashers.py:317 hashers.py:365 hashers.py:393 hashers.py:426
#: hashers.py:459 hashers.py:493
msgid "algorithm"
-msgstr "algorithm"
+msgstr ""
#: hashers.py:244
msgid "iterations"
-msgstr "iterations"
+msgstr ""
#: hashers.py:245 hashers.py:319 hashers.py:366 hashers.py:394 hashers.py:494
msgid "salt"
-msgstr "salt"
+msgstr ""
#: hashers.py:246 hashers.py:367 hashers.py:395 hashers.py:427 hashers.py:460
#: hashers.py:495
msgid "hash"
-msgstr "hash"
+msgstr ""
#: hashers.py:318
msgid "work factor"
-msgstr "work factor"
+msgstr ""
#: hashers.py:320
msgid "checksum"
-msgstr "checksum"
+msgstr ""
#: models.py:72 models.py:121
msgid "name"
-msgstr "name"
+msgstr ""
#: models.py:74
msgid "codename"
-msgstr "codename"
+msgstr ""
#: models.py:78
msgid "permission"
-msgstr "permission"
+msgstr ""
#: models.py:79 models.py:123
msgid "permissions"
-msgstr "permissions"
+msgstr ""
#: models.py:128
msgid "group"
-msgstr "group"
+msgstr ""
-#: models.py:129 models.py:301
+#: models.py:129 models.py:294
msgid "groups"
-msgstr "groups"
+msgstr ""
#: models.py:200
msgid "password"
-msgstr "password"
+msgstr ""
#: models.py:201
msgid "last login"
-msgstr "last login"
+msgstr ""
-#: models.py:298
+#: models.py:291
msgid "superuser status"
-msgstr "superuser status"
+msgstr ""
+
+#: models.py:292
+msgid ""
+"Designates that this user has all permissions without explicitly assigning "
+"them."
+msgstr ""
+
+#: models.py:295
+msgid ""
+"The groups this user belongs to. A user will get all permissions granted to "
+"each of his/her group."
+msgstr ""
#: models.py:299
-msgid ""
-"Designates that this user has all permissions without explicitly assigning "
-"them."
-msgstr ""
-"Designates that this user has all permissions without explicitly assigning "
-"them."
-
-#: models.py:302
-msgid ""
-"The groups this user belongs to. A user will get all permissions granted to "
-"each of his/her group."
-msgstr ""
-"The groups this user belongs to. A user will get all permissions granted to "
-"each of his/her group."
-
-#: models.py:306
msgid "user permissions"
-msgstr "user permissions"
+msgstr ""
-#: models.py:377
+#: models.py:366
msgid "username"
-msgstr "username"
+msgstr ""
+
+#: models.py:367
+msgid ""
+"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"
+msgstr ""
+
+#: models.py:370
+msgid "Enter a valid username."
+msgstr ""
+
+#: models.py:372
+msgid "first name"
+msgstr ""
+
+#: models.py:373
+msgid "last name"
+msgstr ""
+
+#: models.py:374
+msgid "email address"
+msgstr ""
+
+#: models.py:375
+msgid "staff status"
+msgstr ""
+
+#: models.py:376
+msgid "Designates whether the user can log into this admin site."
+msgstr ""
#: models.py:378
-msgid ""
-"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"
+msgid "active"
+msgstr ""
+
+#: models.py:379
+msgid ""
+"Designates whether this user should be treated as active. Unselect this "
+"instead of deleting accounts."
msgstr ""
-"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"
#: models.py:381
-msgid "Enter a valid username."
-msgstr "Enter a valid username."
-
-#: models.py:383
-msgid "first name"
-msgstr "first name"
-
-#: models.py:384
-msgid "last name"
-msgstr "last name"
-
-#: models.py:385
-msgid "email address"
-msgstr "email address"
-
-#: models.py:386
-msgid "staff status"
-msgstr "staff status"
-
-#: models.py:387
-msgid "Designates whether the user can log into this admin site."
-msgstr "Designates whether the user can log into this admin site."
+msgid "date joined"
+msgstr ""
#: models.py:389
-msgid "active"
-msgstr "active"
+msgid "user"
+msgstr ""
#: models.py:390
-msgid ""
-"Designates whether this user should be treated as active. Unselect this "
-"instead of deleting accounts."
-msgstr ""
-"Designates whether this user should be treated as active. Unselect this "
-"instead of deleting accounts."
-
-#: models.py:392
-msgid "date joined"
-msgstr "date joined"
-
-#: models.py:400
-msgid "user"
-msgstr "user"
-
-#: models.py:401
msgid "users"
-msgstr "users"
+msgstr ""
#: views.py:89
msgid "Logged out"
-msgstr "Logged out"
+msgstr ""
#: templates/registration/password_reset_subject.txt:2
#, python-format
msgid "Password reset on %(site_name)s"
-msgstr "Password reset on %(site_name)s"
+msgstr ""
-#: tests/forms.py:325
-#, fuzzy
+#: tests/test_forms.py:325
msgid "Enter a valid email address."
-msgstr "Enter a valid username."
-
-#~ msgid ""
-#~ "Your Web browser doesn't appear to have cookies enabled. Cookies are "
-#~ "required for logging in."
-#~ msgstr ""
-#~ "Your Web browser doesn't appear to have cookies enabled. Cookies are "
-#~ "required for logging in."
-
-#~ msgid ""
-#~ "That email address doesn't have an associated user account. Are you sure "
-#~ "you've registered?"
-#~ msgstr ""
-#~ "That email address doesn't have an associated user account. Are you sure "
-#~ "you've registered?"
-
-#~ msgid ""
-#~ "The user account associated with this email address cannot reset the "
-#~ "password."
-#~ msgstr ""
-#~ "The user account associated with this email address cannot reset the "
-#~ "password."
+msgstr ""
diff --git a/django/contrib/comments/locale/en/LC_MESSAGES/django.mo b/django/contrib/comments/locale/en/LC_MESSAGES/django.mo
index 6a6f6e70b6..08a7b68596 100644
Binary files a/django/contrib/comments/locale/en/LC_MESSAGES/django.mo and b/django/contrib/comments/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/comments/locale/en/LC_MESSAGES/django.po b/django/contrib/comments/locale/en/LC_MESSAGES/django.po
index 980c499dae..6aca84c3cc 100644
--- a/django/contrib/comments/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/comments/locale/en/LC_MESSAGES/django.po
@@ -1,182 +1,171 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-02-14 13:24+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin.py:25
msgid "Content"
-msgstr "Content"
+msgstr ""
#: admin.py:28
msgid "Metadata"
-msgstr "Metadata"
+msgstr ""
#: admin.py:55
-#, fuzzy, python-format
+#, python-format
msgid "%d comment was successfully flagged"
msgid_plural "%d comments were successfully flagged"
-msgstr[0] "1 comment was successfully %(action)s."
-msgstr[1] "%(count)s comments were successfully %(action)s."
+msgstr[0] ""
+msgstr[1] ""
#: admin.py:57
msgid "Flag selected comments"
-msgstr "Flag selected comments"
+msgstr ""
#: admin.py:61
-#, fuzzy, python-format
+#, python-format
msgid "%d comment was successfully approved"
msgid_plural "%d comments were successfully approved"
-msgstr[0] "1 comment was successfully %(action)s."
-msgstr[1] "%(count)s comments were successfully %(action)s."
+msgstr[0] ""
+msgstr[1] ""
#: admin.py:63
msgid "Approve selected comments"
-msgstr "Approve selected comments"
+msgstr ""
#: admin.py:67
-#, fuzzy, python-format
+#, python-format
msgid "%d comment was successfully removed"
msgid_plural "%d comments were successfully removed"
-msgstr[0] "1 comment was successfully %(action)s."
-msgstr[1] "%(count)s comments were successfully %(action)s."
+msgstr[0] ""
+msgstr[1] ""
#: admin.py:69
msgid "Remove selected comments"
-msgstr "Remove selected comments"
+msgstr ""
#: feeds.py:14
#, python-format
msgid "%(site_name)s comments"
-msgstr "%(site_name)s comments"
+msgstr ""
#: feeds.py:20
#, python-format
msgid "Latest comments on %(site_name)s"
-msgstr "Latest comments on %(site_name)s"
+msgstr ""
#: forms.py:96
msgid "Name"
-msgstr "Name"
+msgstr ""
#: forms.py:97
msgid "Email address"
-msgstr "Email address"
+msgstr ""
#: forms.py:98
msgid "URL"
-msgstr "URL"
+msgstr ""
#: forms.py:99
msgid "Comment"
-msgstr "Comment"
+msgstr ""
#: forms.py:177
#, python-format
msgid "Watch your mouth! The word %s is not allowed here."
msgid_plural "Watch your mouth! The words %s are not allowed here."
-msgstr[0] "Watch your mouth! The word %s is not allowed here."
-msgstr[1] "Watch your mouth! The words %s are not allowed here."
+msgstr[0] ""
+msgstr[1] ""
#: forms.py:181 templates/comments/preview.html:16
msgid "and"
-msgstr "and"
+msgstr ""
#: forms.py:186
msgid ""
"If you enter anything in this field your comment will be treated as spam"
msgstr ""
-"If you enter anything in this field your comment will be treated as spam"
#: models.py:23
msgid "content type"
-msgstr "content type"
+msgstr ""
#: models.py:25
msgid "object ID"
-msgstr "object ID"
+msgstr ""
#: models.py:53 models.py:177
msgid "user"
-msgstr "user"
+msgstr ""
#: models.py:55
msgid "user's name"
-msgstr "user's name"
+msgstr ""
#: models.py:56
msgid "user's email address"
-msgstr "user's email address"
+msgstr ""
#: models.py:57
msgid "user's URL"
-msgstr "user's URL"
+msgstr ""
#: models.py:59 models.py:79 models.py:178
msgid "comment"
-msgstr "comment"
+msgstr ""
#: models.py:62
msgid "date/time submitted"
-msgstr "date/time submitted"
+msgstr ""
#: models.py:63
msgid "IP address"
-msgstr "IP address"
+msgstr ""
#: models.py:64
msgid "is public"
-msgstr "is public"
+msgstr ""
#: models.py:65
msgid ""
"Uncheck this box to make the comment effectively disappear from the site."
msgstr ""
-"Uncheck this box to make the comment effectively disappear from the site."
#: models.py:67
msgid "is removed"
-msgstr "is removed"
+msgstr ""
#: models.py:68
msgid ""
"Check this box if the comment is inappropriate. A \"This comment has been "
"removed\" message will be displayed instead."
msgstr ""
-"Check this box if the comment is inappropriate. A \"This comment has been "
-"removed\" message will be displayed instead."
#: models.py:80
msgid "comments"
-msgstr "comments"
+msgstr ""
#: models.py:124
msgid ""
"This comment was posted by an authenticated user and thus the name is read-"
"only."
msgstr ""
-"This comment was posted by an authenticated user and thus the name is read-"
-"only."
#: models.py:134
msgid ""
"This comment was posted by an authenticated user and thus the email is read-"
"only."
msgstr ""
-"This comment was posted by an authenticated user and thus the email is read-"
-"only."
#: models.py:160
#, python-format
@@ -187,128 +176,107 @@ msgid ""
"\n"
"http://%(domain)s%(url)s"
msgstr ""
-"Posted by %(user)s at %(date)s\n"
-"\n"
-"%(comment)s\n"
-"\n"
-"http://%(domain)s%(url)s"
#: models.py:179
msgid "flag"
-msgstr "flag"
+msgstr ""
#: models.py:180
msgid "date"
-msgstr "date"
+msgstr ""
#: models.py:190
msgid "comment flag"
-msgstr "comment flag"
+msgstr ""
#: models.py:191
msgid "comment flags"
-msgstr "comment flags"
+msgstr ""
#: templates/comments/approve.html:4
msgid "Approve a comment"
-msgstr "Approve a comment"
+msgstr ""
#: templates/comments/approve.html:7
msgid "Really make this comment public?"
-msgstr "Really make this comment public?"
+msgstr ""
#: templates/comments/approve.html:12
msgid "Approve"
-msgstr "Approve"
+msgstr ""
#: templates/comments/approved.html:4
msgid "Thanks for approving"
-msgstr "Thanks for approving"
+msgstr ""
#: templates/comments/approved.html:7 templates/comments/deleted.html:7
#: templates/comments/flagged.html:7
msgid ""
"Thanks for taking the time to improve the quality of discussion on our site"
msgstr ""
-"Thanks for taking the time to improve the quality of discussion on our site"
#: templates/comments/delete.html:4
msgid "Remove a comment"
-msgstr "Remove a comment"
+msgstr ""
#: templates/comments/delete.html:7
msgid "Really remove this comment?"
-msgstr "Really remove this comment?"
+msgstr ""
#: templates/comments/delete.html:12
msgid "Remove"
-msgstr "Remove"
+msgstr ""
#: templates/comments/deleted.html:4
msgid "Thanks for removing"
-msgstr "Thanks for removing"
+msgstr ""
#: templates/comments/flag.html:4
msgid "Flag this comment"
-msgstr "Flag this comment"
+msgstr ""
#: templates/comments/flag.html:7
msgid "Really flag this comment?"
-msgstr "Really flag this comment?"
+msgstr ""
#: templates/comments/flag.html:12
msgid "Flag"
-msgstr "Flag"
+msgstr ""
#: templates/comments/flagged.html:4
msgid "Thanks for flagging"
-msgstr "Thanks for flagging"
+msgstr ""
#: templates/comments/form.html:17 templates/comments/preview.html:32
msgid "Post"
-msgstr "Post"
+msgstr ""
#: templates/comments/form.html:18 templates/comments/preview.html:33
msgid "Preview"
-msgstr "Preview"
+msgstr ""
#: templates/comments/posted.html:4
msgid "Thanks for commenting"
-msgstr "Thanks for commenting"
+msgstr ""
#: templates/comments/posted.html:7
msgid "Thank you for your comment"
-msgstr "Thank you for your comment"
+msgstr ""
#: templates/comments/preview.html:4 templates/comments/preview.html.py:13
msgid "Preview your comment"
-msgstr "Preview your comment"
+msgstr ""
#: templates/comments/preview.html:11
msgid "Please correct the error below"
msgid_plural "Please correct the errors below"
-msgstr[0] "Please correct the error below"
-msgstr[1] "Please correct the errors below"
+msgstr[0] ""
+msgstr[1] ""
#: templates/comments/preview.html:16
msgid "Post your comment"
-msgstr "Post your comment"
+msgstr ""
#: templates/comments/preview.html:16
msgid "or make changes"
-msgstr "or make changes"
-
-#~ msgid "flagged"
-#~ msgid_plural "flagged"
-#~ msgstr[0] "flagged"
-#~ msgstr[1] "flagged"
-
-#~ msgid "approved"
-#~ msgid_plural "approved"
-#~ msgstr[0] "approved"
-#~ msgstr[1] "approved"
-
-#~ msgid "removed"
-#~ msgid_plural "removed"
-#~ msgstr[0] "removed"
-#~ msgstr[1] "removed"
+msgstr ""
diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
index 3e132bb3a4..399d24aa87 100644
--- a/django/contrib/contenttypes/generic.py
+++ b/django/contrib/contenttypes/generic.py
@@ -13,8 +13,9 @@ from django.db.models import signals
from django.db.models.fields.related import ForeignObject, ForeignObjectRel
from django.db.models.related import PathInfo
from django.db.models.sql.where import Constraint
-from django.forms import ModelForm
-from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance
+from django.forms import ModelForm, ALL_FIELDS
+from django.forms.models import (BaseModelFormSet, modelformset_factory, save_instance,
+ modelform_defines_fields)
from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
from django.contrib.contenttypes.models import ContentType
from django.utils import six
@@ -137,9 +138,6 @@ class GenericForeignKey(six.with_metaclass(RenameGenericForeignKeyMethods)):
return rel_obj
def __set__(self, instance, value):
- if instance is None:
- raise AttributeError("%s must be accessed via instance" % self.related.opts.object_name)
-
ct = None
fk = None
if value is not None:
@@ -280,9 +278,6 @@ class ReverseGenericRelatedObjectsDescriptor(object):
return manager
def __set__(self, instance, value):
- if instance is None:
- raise AttributeError("Manager must be accessed via instance")
-
manager = self.__get__(instance)
manager.clear()
for obj in value:
@@ -486,6 +481,10 @@ class GenericInlineModelAdmin(InlineModelAdmin):
"exclude": exclude
}
defaults.update(kwargs)
+
+ if defaults['fields'] is None and not modelform_defines_fields(defaults['form']):
+ defaults['fields'] = ALL_FIELDS
+
return generic_inlineformset_factory(self.model, **defaults)
class GenericStackedInline(GenericInlineModelAdmin):
diff --git a/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.mo b/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.mo
index 68b8aa62be..08a7b68596 100644
Binary files a/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.mo and b/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.po b/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.po
index 03bedfadd8..0ca678d9cc 100644
--- a/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.po
@@ -1,44 +1,41 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-03-08 11:45+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: models.py:130
msgid "python model class name"
-msgstr "python model class name"
+msgstr ""
#: models.py:134
msgid "content type"
-msgstr "content type"
+msgstr ""
#: models.py:135
msgid "content types"
-msgstr "content types"
+msgstr ""
#: views.py:17
#, python-format
msgid "Content type %(ct_id)s object has no associated model"
-msgstr "Content type %(ct_id)s object has no associated model"
+msgstr ""
#: views.py:21
#, python-format
msgid "Content type %(ct_id)s object %(obj_id)s doesn't exist"
-msgstr "Content type %(ct_id)s object %(obj_id)s doesn't exist"
+msgstr ""
#: views.py:27
#, python-format
msgid "%(ct_name)s objects don't have a get_absolute_url() method"
-msgstr "%(ct_name)s objects don't have a get_absolute_url() method"
+msgstr ""
diff --git a/django/contrib/flatpages/forms.py b/django/contrib/flatpages/forms.py
index a848875a9f..80938116ad 100644
--- a/django/contrib/flatpages/forms.py
+++ b/django/contrib/flatpages/forms.py
@@ -12,6 +12,7 @@ class FlatpageForm(forms.ModelForm):
class Meta:
model = FlatPage
+ fields = '__all__'
def clean_url(self):
url = self.cleaned_data['url']
diff --git a/django/contrib/flatpages/locale/en/LC_MESSAGES/django.mo b/django/contrib/flatpages/locale/en/LC_MESSAGES/django.mo
index 67acb769d7..08a7b68596 100644
Binary files a/django/contrib/flatpages/locale/en/LC_MESSAGES/django.mo and b/django/contrib/flatpages/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/flatpages/locale/en/LC_MESSAGES/django.po b/django/contrib/flatpages/locale/en/LC_MESSAGES/django.po
index e671dda5fc..53b82e5793 100644
--- a/django/contrib/flatpages/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/flatpages/locale/en/LC_MESSAGES/django.po
@@ -1,97 +1,88 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-10-18 10:56+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin.py:10
msgid "Advanced options"
-msgstr "Advanced options"
+msgstr ""
#: forms.py:7 models.py:11
msgid "URL"
-msgstr "URL"
+msgstr ""
#: forms.py:8
msgid ""
"Example: '/about/contact/'. Make sure to have leading and trailing slashes."
msgstr ""
-"Example: '/about/contact/'. Make sure to have leading and trailing slashes."
#: forms.py:10
msgid ""
"This value must contain only letters, numbers, dots, underscores, dashes, "
"slashes or tildes."
msgstr ""
-"This value must contain only letters, numbers, dots, underscores, dashes, "
-"slashes or tildes."
#: forms.py:19
msgid "URL is missing a leading slash."
-msgstr "URL is missing a leading slash."
+msgstr ""
#: forms.py:23
msgid "URL is missing a trailing slash."
-msgstr "URL is missing a trailing slash."
+msgstr ""
#: forms.py:38
#, python-format
msgid "Flatpage with url %(url)s already exists for site %(site)s"
-msgstr "Flatpage with url %(url)s already exists for site %(site)s"
+msgstr ""
#: models.py:12
msgid "title"
-msgstr "title"
+msgstr ""
#: models.py:13
msgid "content"
-msgstr "content"
+msgstr ""
#: models.py:14
msgid "enable comments"
-msgstr "enable comments"
+msgstr ""
#: models.py:15
msgid "template name"
-msgstr "template name"
+msgstr ""
#: models.py:16
msgid ""
"Example: 'flatpages/contact_page.html'. If this isn't provided, the system "
"will use 'flatpages/default.html'."
msgstr ""
-"Example: 'flatpages/contact_page.html'. If this isn't provided, the system "
-"will use 'flatpages/default.html'."
#: models.py:17
msgid "registration required"
-msgstr "registration required"
+msgstr ""
#: models.py:18
msgid "If this is checked, only logged-in users will be able to view the page."
msgstr ""
-"If this is checked, only logged-in users will be able to view the page."
#: models.py:24
msgid "flat page"
-msgstr "flat page"
+msgstr ""
#: models.py:25
msgid "flat pages"
-msgstr "flat pages"
+msgstr ""
-#: tests/forms.py:98
+#: tests/test_forms.py:98
msgid "This field is required."
-msgstr "This field is required."
+msgstr ""
diff --git a/django/contrib/formtools/locale/en/LC_MESSAGES/django.mo b/django/contrib/formtools/locale/en/LC_MESSAGES/django.mo
index 64ae715f31..08a7b68596 100644
Binary files a/django/contrib/formtools/locale/en/LC_MESSAGES/django.mo and b/django/contrib/formtools/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/formtools/locale/en/LC_MESSAGES/django.po b/django/contrib/formtools/locale/en/LC_MESSAGES/django.po
index e3b7dd7557..048b9fea45 100644
--- a/django/contrib/formtools/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/formtools/locale/en/LC_MESSAGES/django.po
@@ -1,36 +1,26 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-02-14 13:42+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: templates/formtools/wizard/wizard_form.html:15
msgid "first step"
-msgstr "first step"
+msgstr ""
#: templates/formtools/wizard/wizard_form.html:16
msgid "prev step"
-msgstr "prev step"
+msgstr ""
#: templates/formtools/wizard/wizard_form.html:18
msgid "submit"
-msgstr "submit"
-
-#~ msgid ""
-#~ "We apologize, but your form has expired. Please continue filling out the "
-#~ "form from this page."
-#~ msgstr ""
-#~ "We apologize, but your form has expired. Please continue filling out the "
-#~ "form from this page."
+msgstr ""
diff --git a/django/contrib/formtools/tests/wizard/test_forms.py b/django/contrib/formtools/tests/wizard/test_forms.py
index 14c6e6a685..7425755cf5 100644
--- a/django/contrib/formtools/tests/wizard/test_forms.py
+++ b/django/contrib/formtools/tests/wizard/test_forms.py
@@ -60,9 +60,11 @@ class TestModel(models.Model):
class TestModelForm(forms.ModelForm):
class Meta:
model = TestModel
+ fields = '__all__'
-TestModelFormSet = forms.models.modelformset_factory(TestModel, form=TestModelForm, extra=2)
+TestModelFormSet = forms.models.modelformset_factory(TestModel, form=TestModelForm, extra=2,
+ fields='__all__')
class TestWizard(WizardView):
diff --git a/django/contrib/formtools/tests/wizard/wizardtests/tests.py b/django/contrib/formtools/tests/wizard/wizardtests/tests.py
index 1ee5dbdc78..3c2dbc3efb 100644
--- a/django/contrib/formtools/tests/wizard/wizardtests/tests.py
+++ b/django/contrib/formtools/tests/wizard/wizardtests/tests.py
@@ -15,6 +15,7 @@ from django.utils._os import upath
class UserForm(forms.ModelForm):
class Meta:
model = User
+ fields = '__all__'
UserFormSet = forms.models.modelformset_factory(User, form=UserForm, extra=2)
diff --git a/django/contrib/gis/db/backends/base.py b/django/contrib/gis/db/backends/base.py
index 171a304439..7db7ce51ba 100644
--- a/django/contrib/gis/db/backends/base.py
+++ b/django/contrib/gis/db/backends/base.py
@@ -3,10 +3,12 @@ Base/mixin classes for the spatial backend database operations and the
`SpatialRefSys` model the backend.
"""
import re
+
from django.contrib.gis import gdal
from django.utils import six
from django.utils.encoding import python_2_unicode_compatible
+
class BaseSpatialOperations(object):
"""
This module holds the base `BaseSpatialBackend` object, which is
@@ -18,7 +20,7 @@ class BaseSpatialOperations(object):
geometry_operators = {}
geography_operators = {}
geography_functions = {}
- gis_terms = {}
+ gis_terms = set()
truncate_params = {}
# Quick booleans for the type of this spatial backend, and
diff --git a/django/contrib/gis/db/backends/mysql/operations.py b/django/contrib/gis/db/backends/mysql/operations.py
index 14402ec0a3..26cec743a9 100644
--- a/django/contrib/gis/db/backends/mysql/operations.py
+++ b/django/contrib/gis/db/backends/mysql/operations.py
@@ -3,7 +3,6 @@ from django.db.backends.mysql.base import DatabaseOperations
from django.contrib.gis.db.backends.adapter import WKTAdapter
from django.contrib.gis.db.backends.base import BaseSpatialOperations
-from django.utils import six
class MySQLOperations(DatabaseOperations, BaseSpatialOperations):
@@ -18,21 +17,21 @@ class MySQLOperations(DatabaseOperations, BaseSpatialOperations):
Adaptor = Adapter # Backwards-compatibility alias.
geometry_functions = {
- 'bbcontains' : 'MBRContains', # For consistency w/PostGIS API
- 'bboverlaps' : 'MBROverlaps', # .. ..
- 'contained' : 'MBRWithin', # .. ..
- 'contains' : 'MBRContains',
- 'disjoint' : 'MBRDisjoint',
- 'equals' : 'MBREqual',
- 'exact' : 'MBREqual',
- 'intersects' : 'MBRIntersects',
- 'overlaps' : 'MBROverlaps',
- 'same_as' : 'MBREqual',
- 'touches' : 'MBRTouches',
- 'within' : 'MBRWithin',
- }
+ 'bbcontains': 'MBRContains', # For consistency w/PostGIS API
+ 'bboverlaps': 'MBROverlaps', # .. ..
+ 'contained': 'MBRWithin', # .. ..
+ 'contains': 'MBRContains',
+ 'disjoint': 'MBRDisjoint',
+ 'equals': 'MBREqual',
+ 'exact': 'MBREqual',
+ 'intersects': 'MBRIntersects',
+ 'overlaps': 'MBROverlaps',
+ 'same_as': 'MBREqual',
+ 'touches': 'MBRTouches',
+ 'within': 'MBRWithin',
+ }
- gis_terms = dict([(term, None) for term in list(geometry_functions) + ['isnull']])
+ gis_terms = set(geometry_functions) | set(['isnull'])
def geo_db_type(self, f):
return f.geom_type
diff --git a/django/contrib/gis/db/backends/oracle/operations.py b/django/contrib/gis/db/backends/oracle/operations.py
index 18697ac8c0..84217c331b 100644
--- a/django/contrib/gis/db/backends/oracle/operations.py
+++ b/django/contrib/gis/db/backends/oracle/operations.py
@@ -18,6 +18,7 @@ from django.contrib.gis.geometry.backend import Geometry
from django.contrib.gis.measure import Distance
from django.utils import six
+
class SDOOperation(SpatialFunction):
"Base class for SDO* Oracle operations."
sql_template = "%(function)s(%(geo_col)s, %(geometry)s) %(operator)s '%(result)s'"
@@ -32,6 +33,7 @@ class SDODistance(SpatialFunction):
sql_template = ('%(function)s(%(geo_col)s, %(geometry)s, %(tolerance)s) '
'%(operator)s %(result)s')
dist_func = 'SDO_GEOM.SDO_DISTANCE'
+
def __init__(self, op, tolerance=0.05):
super(SDODistance, self).__init__(self.dist_func,
tolerance=tolerance,
@@ -40,6 +42,7 @@ class SDODistance(SpatialFunction):
class SDODWithin(SpatialFunction):
dwithin_func = 'SDO_WITHIN_DISTANCE'
sql_template = "%(function)s(%(geo_col)s, %(geometry)s, %%s) = 'TRUE'"
+
def __init__(self):
super(SDODWithin, self).__init__(self.dwithin_func)
@@ -48,6 +51,7 @@ class SDOGeomRelate(SpatialFunction):
relate_func = 'SDO_GEOM.RELATE'
sql_template = ("%(function)s(%(geo_col)s, '%(mask)s', %(geometry)s, "
"%(tolerance)s) %(operator)s '%(mask)s'")
+
def __init__(self, mask, tolerance=0.05):
# SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance.
# Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE').
@@ -60,6 +64,7 @@ class SDORelate(SpatialFunction):
mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I)
sql_template = "%(function)s(%(geo_col)s, %(geometry)s, 'mask=%(mask)s') = 'TRUE'"
relate_func = 'SDO_RELATE'
+
def __init__(self, mask):
if not self.mask_regex.match(mask):
raise ValueError('Invalid %s mask: "%s"' % (self.relate_func, mask))
@@ -79,12 +84,12 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations):
Adaptor = Adapter # Backwards-compatibility alias.
area = 'SDO_GEOM.SDO_AREA'
- gml= 'SDO_UTIL.TO_GMLGEOMETRY'
+ gml = 'SDO_UTIL.TO_GMLGEOMETRY'
centroid = 'SDO_GEOM.SDO_CENTROID'
difference = 'SDO_GEOM.SDO_DIFFERENCE'
distance = 'SDO_GEOM.SDO_DISTANCE'
- extent= 'SDO_AGGR_MBR'
- intersection= 'SDO_GEOM.SDO_INTERSECTION'
+ extent = 'SDO_AGGR_MBR'
+ intersection = 'SDO_GEOM.SDO_INTERSECTION'
length = 'SDO_GEOM.SDO_LENGTH'
num_geom = 'SDO_UTIL.GETNUMELEM'
num_points = 'SDO_UTIL.GETNUMVERTICES'
@@ -127,9 +132,8 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations):
}
geometry_functions.update(distance_functions)
- gis_terms = ['isnull']
- gis_terms += list(geometry_functions)
- gis_terms = dict([(term, None) for term in gis_terms])
+ gis_terms = set(['isnull'])
+ gis_terms.update(geometry_functions)
truncate_params = {'relate' : None}
@@ -272,7 +276,8 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations):
given Aggregate instance.
"""
agg_name = agg.__class__.__name__.lower()
- if agg_name == 'union' : agg_name += 'agg'
+ if agg_name == 'union':
+ agg_name += 'agg'
if agg.is_extent:
sql_template = '%(function)s(%(field)s)'
else:
@@ -295,5 +300,5 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations):
"""
# This code doesn't work for bulk insert cases.
assert len(placeholders) == 1
- return [[param for pholder,param
+ return [[param for pholder, param
in six.moves.zip(placeholders[0], params[0]) if pholder != 'NULL'], ]
diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py
index 4d31bbb53c..734f39c752 100644
--- a/django/contrib/gis/db/backends/postgis/operations.py
+++ b/django/contrib/gis/db/backends/postgis/operations.py
@@ -208,23 +208,22 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
# for the geography type.
self.geography_functions = self.distance_functions.copy()
self.geography_functions.update({
- 'coveredby' : self.geometry_functions['coveredby'],
- 'covers' : self.geometry_functions['covers'],
- 'intersects' : self.geometry_functions['intersects'],
- })
+ 'coveredby': self.geometry_functions['coveredby'],
+ 'covers': self.geometry_functions['covers'],
+ 'intersects': self.geometry_functions['intersects'],
+ })
self.geography_operators = {
- 'bboverlaps' : PostGISOperator('&&'),
- }
+ 'bboverlaps': PostGISOperator('&&'),
+ }
# Native geometry type support added in PostGIS 2.0.
if version >= (2, 0, 0):
self.geometry = True
# Creating a dictionary lookup of all GIS terms for PostGIS.
- gis_terms = ['isnull']
- gis_terms += list(self.geometry_operators)
- gis_terms += list(self.geometry_functions)
- self.gis_terms = dict([(term, None) for term in gis_terms])
+ self.gis_terms = set(['isnull'])
+ self.gis_terms.update(self.geometry_operators)
+ self.gis_terms.update(self.geometry_functions)
self.area = prefix + 'Area'
self.bounding_circle = BOUNDINGCIRCLE
@@ -247,7 +246,7 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
self.makeline = prefix + 'MakeLine'
self.mem_size = prefix + 'mem_size'
self.num_geom = prefix + 'NumGeometries'
- self.num_points =prefix + 'npoints'
+ self.num_points = prefix + 'npoints'
self.perimeter = prefix + 'Perimeter'
self.point_on_surface = prefix + 'PointOnSurface'
self.polygonize = prefix + 'Polygonize'
@@ -324,7 +323,7 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
raise NotImplementedError('PostGIS 1.5 supports geography columns '
'only with an SRID of 4326.')
- return 'geography(%s,%d)'% (f.geom_type, f.srid)
+ return 'geography(%s,%d)' % (f.geom_type, f.srid)
elif self.geometry:
# Postgis 2.0 supports type-based geometries.
# TODO: Support 'M' extension.
@@ -565,7 +564,8 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
if not self.check_aggregate_support(agg):
raise NotImplementedError('%s spatial aggregate is not implmented for this backend.' % agg_name)
agg_name = agg_name.lower()
- if agg_name == 'union': agg_name += 'agg'
+ if agg_name == 'union':
+ agg_name += 'agg'
sql_template = '%(function)s(%(field)s)'
sql_function = getattr(self, agg_name)
return sql_template, sql_function
diff --git a/django/contrib/gis/db/backends/spatialite/operations.py b/django/contrib/gis/db/backends/spatialite/operations.py
index 83745476a0..4281cafa23 100644
--- a/django/contrib/gis/db/backends/spatialite/operations.py
+++ b/django/contrib/gis/db/backends/spatialite/operations.py
@@ -117,9 +117,8 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
super(DatabaseOperations, self).__init__(connection)
# Creating the GIS terms dictionary.
- gis_terms = ['isnull']
- gis_terms += self.geometry_functions.keys()
- self.gis_terms = dict([(term, None) for term in gis_terms])
+ self.gis_terms = set(['isnull'])
+ self.gis_terms.update(self.geometry_functions)
@cached_property
def spatial_version(self):
diff --git a/django/contrib/gis/geoip/base.py b/django/contrib/gis/geoip/base.py
index 4b8b6e1ed3..d4793a2ae9 100644
--- a/django/contrib/gis/geoip/base.py
+++ b/django/contrib/gis/geoip/base.py
@@ -11,6 +11,7 @@ from django.contrib.gis.geoip.prototypes import (
GeoIP_country_name_by_addr, GeoIP_country_name_by_name)
from django.utils import six
+from django.utils.encoding import force_bytes
# Regular expressions for recognizing the GeoIP free database editions.
free_regex = re.compile(r'^GEO-\d{3}FREE')
@@ -97,18 +98,18 @@ class GeoIP(object):
# and/or city datasets exist, then try and open them.
country_db = os.path.join(path, country or GEOIP_SETTINGS.get('GEOIP_COUNTRY', 'GeoIP.dat'))
if os.path.isfile(country_db):
- self._country = GeoIP_open(country_db, cache)
+ self._country = GeoIP_open(force_bytes(country_db), cache)
self._country_file = country_db
city_db = os.path.join(path, city or GEOIP_SETTINGS.get('GEOIP_CITY', 'GeoLiteCity.dat'))
if os.path.isfile(city_db):
- self._city = GeoIP_open(city_db, cache)
+ self._city = GeoIP_open(force_bytes(city_db), cache)
self._city_file = city_db
elif os.path.isfile(path):
# Otherwise, some detective work will be needed to figure
# out whether the given database path is for the GeoIP country
# or city databases.
- ptr = GeoIP_open(path, cache)
+ ptr = GeoIP_open(force_bytes(path), cache)
info = GeoIP_database_info(ptr)
if lite_regex.match(info):
# GeoLite City database detected.
@@ -136,9 +137,6 @@ class GeoIP(object):
if not isinstance(query, six.string_types):
raise TypeError('GeoIP query must be a string, not type %s' % type(query).__name__)
- # GeoIP only takes ASCII-encoded strings.
- query = query.encode('ascii')
-
# Extra checks for the existence of country and city databases.
if city_or_country and not (self._country or self._city):
raise GeoIPException('Invalid GeoIP country and city data files.')
@@ -147,8 +145,8 @@ class GeoIP(object):
elif city and not self._city:
raise GeoIPException('Invalid GeoIP city data file: %s' % self._city_file)
- # Return the query string back to the caller.
- return query
+ # Return the query string back to the caller. GeoIP only takes bytestrings.
+ return force_bytes(query)
def city(self, query):
"""
@@ -156,33 +154,33 @@ class GeoIP(object):
Fully Qualified Domain Name (FQDN). Some information in the dictionary
may be undefined (None).
"""
- query = self._check_query(query, city=True)
+ enc_query = self._check_query(query, city=True)
if ipv4_re.match(query):
# If an IP address was passed in
- return GeoIP_record_by_addr(self._city, c_char_p(query))
+ return GeoIP_record_by_addr(self._city, c_char_p(enc_query))
else:
# If a FQDN was passed in.
- return GeoIP_record_by_name(self._city, c_char_p(query))
+ return GeoIP_record_by_name(self._city, c_char_p(enc_query))
def country_code(self, query):
"Returns the country code for the given IP Address or FQDN."
- query = self._check_query(query, city_or_country=True)
+ enc_query = self._check_query(query, city_or_country=True)
if self._country:
if ipv4_re.match(query):
- return GeoIP_country_code_by_addr(self._country, query)
+ return GeoIP_country_code_by_addr(self._country, enc_query)
else:
- return GeoIP_country_code_by_name(self._country, query)
+ return GeoIP_country_code_by_name(self._country, enc_query)
else:
return self.city(query)['country_code']
def country_name(self, query):
"Returns the country name for the given IP Address or FQDN."
- query = self._check_query(query, city_or_country=True)
+ enc_query = self._check_query(query, city_or_country=True)
if self._country:
if ipv4_re.match(query):
- return GeoIP_country_name_by_addr(self._country, query)
+ return GeoIP_country_name_by_addr(self._country, enc_query)
else:
- return GeoIP_country_name_by_name(self._country, query)
+ return GeoIP_country_name_by_name(self._country, enc_query)
else:
return self.city(query)['country_name']
diff --git a/django/contrib/gis/geoip/prototypes.py b/django/contrib/gis/geoip/prototypes.py
index 1cec0d5c24..283d721395 100644
--- a/django/contrib/gis/geoip/prototypes.py
+++ b/django/contrib/gis/geoip/prototypes.py
@@ -92,7 +92,7 @@ def check_string(result, func, cargs):
free(result)
else:
s = ''
- return s
+ return s.decode()
GeoIP_database_info = lgeoip.GeoIP_database_info
GeoIP_database_info.restype = geoip_char_p
@@ -100,7 +100,12 @@ GeoIP_database_info.errcheck = check_string
# String output routines.
def string_output(func):
+ def _err_check(result, func, cargs):
+ if result:
+ return result.decode()
+ return result
func.restype = c_char_p
+ func.errcheck = _err_check
return func
GeoIP_country_code_by_addr = string_output(lgeoip.GeoIP_country_code_by_addr)
diff --git a/django/contrib/gis/geoip/tests.py b/django/contrib/gis/geoip/tests.py
index c890c4f4ba..bb4a3e7e23 100644
--- a/django/contrib/gis/geoip/tests.py
+++ b/django/contrib/gis/geoip/tests.py
@@ -103,14 +103,8 @@ class GeoIPTest(unittest.TestCase):
def test05_unicode_response(self):
"Testing that GeoIP strings are properly encoded, see #16553."
g = GeoIP()
- d = g.city('62.224.93.23')
- self.assertEqual('SchÃŒmberg', d['city'])
-
- def test06_unicode_query(self):
- "Testing that GeoIP accepts unicode string queries, see #17059."
- g = GeoIP()
- d = g.country('whitehouse.gov')
- self.assertEqual('US', d['country_code'])
+ d = g.city("www.osnabrueck.de")
+ self.assertEqual('OsnabrÃŒck', d['city'])
def suite():
diff --git a/django/contrib/gis/locale/en/LC_MESSAGES/django.mo b/django/contrib/gis/locale/en/LC_MESSAGES/django.mo
index 19ac4a3470..08a7b68596 100644
Binary files a/django/contrib/gis/locale/en/LC_MESSAGES/django.mo and b/django/contrib/gis/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/gis/locale/en/LC_MESSAGES/django.po b/django/contrib/gis/locale/en/LC_MESSAGES/django.po
index ed57f6aa9c..6c8d7ec92a 100644
--- a/django/contrib/gis/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/gis/locale/en/LC_MESSAGES/django.po
@@ -1,93 +1,88 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-03-08 12:32+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: views.py:9
msgid "No feeds are registered."
-msgstr "No feeds are registered."
+msgstr ""
#: views.py:19
#, python-format
msgid "Slug %r isn't registered."
-msgstr "Slug %r isn't registered."
+msgstr ""
#: db/models/fields.py:51
msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type."
-msgstr "The base GIS field -- maps to the OpenGIS Specification Geometry type."
+msgstr ""
#: db/models/fields.py:270
msgid "Point"
-msgstr "Point"
+msgstr ""
#: db/models/fields.py:274
msgid "Line string"
-msgstr "Line string"
+msgstr ""
#: db/models/fields.py:278
msgid "Polygon"
-msgstr "Polygon"
+msgstr ""
#: db/models/fields.py:282
msgid "Multi-point"
-msgstr "Multi-point"
+msgstr ""
#: db/models/fields.py:286
msgid "Multi-line string"
-msgstr "Multi-line string"
+msgstr ""
#: db/models/fields.py:290
msgid "Multi polygon"
-msgstr "Multi polygon"
+msgstr ""
#: db/models/fields.py:294
msgid "Geometry collection"
-msgstr "Geometry collection"
+msgstr ""
#: forms/fields.py:23
msgid "No geometry value provided."
-msgstr "No geometry value provided."
+msgstr ""
#: forms/fields.py:24
msgid "Invalid geometry value."
-msgstr "Invalid geometry value."
+msgstr ""
#: forms/fields.py:25
msgid "Invalid geometry type."
-msgstr "Invalid geometry type."
+msgstr ""
#: forms/fields.py:26
msgid ""
"An error occurred when transforming the geometry to the SRID of the geometry "
"form field."
msgstr ""
-"An error occurred when transforming the geometry to the SRID of the geometry "
-"form field."
#: sitemaps/views.py:46
#, python-format
msgid "No sitemap available for section: %r"
-msgstr "No sitemap available for section: %r"
+msgstr ""
#: sitemaps/views.py:60
#, python-format
msgid "Page %s empty"
-msgstr "Page %s empty"
+msgstr ""
#: sitemaps/views.py:62
#, python-format
msgid "No page '%s'"
-msgstr "No page '%s'"
+msgstr ""
diff --git a/django/contrib/gis/tests/geoapp/tests.py b/django/contrib/gis/tests/geoapp/tests.py
index a5164d39d1..672d78c1dd 100644
--- a/django/contrib/gis/tests/geoapp/tests.py
+++ b/django/contrib/gis/tests/geoapp/tests.py
@@ -297,7 +297,7 @@ class GeoLookupTest(TestCase):
# The left/right lookup tests are known failures on PostGIS 2.0/2.0.1
# http://trac.osgeo.org/postgis/ticket/2035
- if (2, 0, 0) <= connection.ops.spatial_version <= (2, 0, 1):
+ if connection.ops.postgis and (2, 0, 0) <= connection.ops.spatial_version <= (2, 0, 1):
test_left_right_lookups = unittest.expectedFailure(test_left_right_lookups)
def test_equals_lookups(self):
diff --git a/django/contrib/humanize/locale/en/LC_MESSAGES/django.mo b/django/contrib/humanize/locale/en/LC_MESSAGES/django.mo
index 55110ebd78..08a7b68596 100644
Binary files a/django/contrib/humanize/locale/en/LC_MESSAGES/django.mo and b/django/contrib/humanize/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/humanize/locale/en/LC_MESSAGES/django.po b/django/contrib/humanize/locale/en/LC_MESSAGES/django.po
index a4b6138e7f..fc75b677a0 100644
--- a/django/contrib/humanize/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/humanize/locale/en/LC_MESSAGES/django.po
@@ -1,293 +1,290 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-02-14 13:08+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: tests.py:131 templatetags/humanize.py:168
+#: tests.py:135 templatetags/humanize.py:168
msgid "today"
-msgstr "today"
+msgstr ""
-#: tests.py:131 templatetags/humanize.py:172
+#: tests.py:135 templatetags/humanize.py:172
msgid "yesterday"
-msgstr "yesterday"
+msgstr ""
-#: tests.py:131 templatetags/humanize.py:170
+#: tests.py:135 templatetags/humanize.py:170
msgid "tomorrow"
-msgstr "tomorrow"
+msgstr ""
#: templatetags/humanize.py:26
msgid "th"
-msgstr "th"
+msgstr ""
#: templatetags/humanize.py:26
msgid "st"
-msgstr "st"
+msgstr ""
#: templatetags/humanize.py:26
msgid "nd"
-msgstr "nd"
+msgstr ""
#: templatetags/humanize.py:26
msgid "rd"
-msgstr "rd"
+msgstr ""
#: templatetags/humanize.py:55
#, python-format
msgid "%(value).1f million"
msgid_plural "%(value).1f million"
-msgstr[0] "%(value).1f million"
-msgstr[1] "%(value).1f million"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:56
#, python-format
msgid "%(value)s million"
msgid_plural "%(value)s million"
-msgstr[0] "%(value)s million"
-msgstr[1] "%(value)s million"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:59
#, python-format
msgid "%(value).1f billion"
msgid_plural "%(value).1f billion"
-msgstr[0] "%(value).1f billion"
-msgstr[1] "%(value).1f billion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:60
#, python-format
msgid "%(value)s billion"
msgid_plural "%(value)s billion"
-msgstr[0] "%(value)s billion"
-msgstr[1] "%(value)s billion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:63
#, python-format
msgid "%(value).1f trillion"
msgid_plural "%(value).1f trillion"
-msgstr[0] "%(value).1f trillion"
-msgstr[1] "%(value).1f trillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:64
#, python-format
msgid "%(value)s trillion"
msgid_plural "%(value)s trillion"
-msgstr[0] "%(value)s trillion"
-msgstr[1] "%(value)s trillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:67
#, python-format
msgid "%(value).1f quadrillion"
msgid_plural "%(value).1f quadrillion"
-msgstr[0] "%(value).1f quadrillion"
-msgstr[1] "%(value).1f quadrillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:68
#, python-format
msgid "%(value)s quadrillion"
msgid_plural "%(value)s quadrillion"
-msgstr[0] "%(value)s quadrillion"
-msgstr[1] "%(value)s quadrillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:71
#, python-format
msgid "%(value).1f quintillion"
msgid_plural "%(value).1f quintillion"
-msgstr[0] "%(value).1f quintillion"
-msgstr[1] "%(value).1f quintillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:72
#, python-format
msgid "%(value)s quintillion"
msgid_plural "%(value)s quintillion"
-msgstr[0] "%(value)s quintillion"
-msgstr[1] "%(value)s quintillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:75
#, python-format
msgid "%(value).1f sextillion"
msgid_plural "%(value).1f sextillion"
-msgstr[0] "%(value).1f sextillion"
-msgstr[1] "%(value).1f sextillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:76
#, python-format
msgid "%(value)s sextillion"
msgid_plural "%(value)s sextillion"
-msgstr[0] "%(value)s sextillion"
-msgstr[1] "%(value)s sextillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:79
#, python-format
msgid "%(value).1f septillion"
msgid_plural "%(value).1f septillion"
-msgstr[0] "%(value).1f septillion"
-msgstr[1] "%(value).1f septillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:80
#, python-format
msgid "%(value)s septillion"
msgid_plural "%(value)s septillion"
-msgstr[0] "%(value)s septillion"
-msgstr[1] "%(value)s septillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:83
#, python-format
msgid "%(value).1f octillion"
msgid_plural "%(value).1f octillion"
-msgstr[0] "%(value).1f octillion"
-msgstr[1] "%(value).1f octillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:84
#, python-format
msgid "%(value)s octillion"
msgid_plural "%(value)s octillion"
-msgstr[0] "%(value)s octillion"
-msgstr[1] "%(value)s octillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:87
#, python-format
msgid "%(value).1f nonillion"
msgid_plural "%(value).1f nonillion"
-msgstr[0] "%(value).1f nonillion"
-msgstr[1] "%(value).1f nonillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:88
#, python-format
msgid "%(value)s nonillion"
msgid_plural "%(value)s nonillion"
-msgstr[0] "%(value)s nonillion"
-msgstr[1] "%(value)s nonillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:91
#, python-format
msgid "%(value).1f decillion"
msgid_plural "%(value).1f decillion"
-msgstr[0] "%(value).1f decillion"
-msgstr[1] "%(value).1f decillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:92
#, python-format
msgid "%(value)s decillion"
msgid_plural "%(value)s decillion"
-msgstr[0] "%(value)s decillion"
-msgstr[1] "%(value)s decillion"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:95
#, python-format
msgid "%(value).1f googol"
msgid_plural "%(value).1f googol"
-msgstr[0] "%(value).1f googol"
-msgstr[1] "%(value).1f googol"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:96
#, python-format
msgid "%(value)s googol"
msgid_plural "%(value)s googol"
-msgstr[0] "%(value)s googol"
-msgstr[1] "%(value)s googol"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:145
msgid "one"
-msgstr "one"
+msgstr ""
#: templatetags/humanize.py:145
msgid "two"
-msgstr "two"
+msgstr ""
#: templatetags/humanize.py:145
msgid "three"
-msgstr "three"
+msgstr ""
#: templatetags/humanize.py:145
msgid "four"
-msgstr "four"
+msgstr ""
#: templatetags/humanize.py:145
msgid "five"
-msgstr "five"
+msgstr ""
#: templatetags/humanize.py:145
msgid "six"
-msgstr "six"
+msgstr ""
#: templatetags/humanize.py:145
msgid "seven"
-msgstr "seven"
+msgstr ""
#: templatetags/humanize.py:145
msgid "eight"
-msgstr "eight"
+msgstr ""
#: templatetags/humanize.py:145
msgid "nine"
-msgstr "nine"
+msgstr ""
#: templatetags/humanize.py:191
#, python-format
msgctxt "naturaltime"
msgid "%(delta)s ago"
-msgstr "%(delta)s ago"
+msgstr ""
#: templatetags/humanize.py:194 templatetags/humanize.py:216
msgid "now"
-msgstr "now"
+msgstr ""
#: templatetags/humanize.py:197
#, python-format
msgid "a second ago"
msgid_plural "%(count)s seconds ago"
-msgstr[0] "a second ago"
-msgstr[1] "%(count)s seconds ago"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:202
#, python-format
msgid "a minute ago"
msgid_plural "%(count)s minutes ago"
-msgstr[0] "a minute ago"
-msgstr[1] "%(count)s minutes ago"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:207
#, python-format
msgid "an hour ago"
msgid_plural "%(count)s hours ago"
-msgstr[0] "an hour ago"
-msgstr[1] "%(count)s hours ago"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:213
#, python-format
msgctxt "naturaltime"
msgid "%(delta)s from now"
-msgstr "%(delta)s from now"
+msgstr ""
#: templatetags/humanize.py:219
#, python-format
msgid "a second from now"
msgid_plural "%(count)s seconds from now"
-msgstr[0] "a second from now"
-msgstr[1] "%(count)s seconds from now"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:224
#, python-format
msgid "a minute from now"
msgid_plural "%(count)s minutes from now"
-msgstr[0] "a minute from now"
-msgstr[1] "%(count)s minutes from now"
+msgstr[0] ""
+msgstr[1] ""
#: templatetags/humanize.py:229
#, python-format
msgid "an hour from now"
msgid_plural "%(count)s hours from now"
-msgstr[0] "an hour from now"
-msgstr[1] "%(count)s hours from now"
+msgstr[0] ""
+msgstr[1] ""
diff --git a/django/contrib/messages/locale/en/LC_MESSAGES/django.mo b/django/contrib/messages/locale/en/LC_MESSAGES/django.mo
index a0c5646f66..08a7b68596 100644
Binary files a/django/contrib/messages/locale/en/LC_MESSAGES/django.mo and b/django/contrib/messages/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/messages/locale/en/LC_MESSAGES/django.po b/django/contrib/messages/locale/en/LC_MESSAGES/django.po
index b1c1f9bea4..7a041aa1a2 100644
--- a/django/contrib/messages/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/messages/locale/en/LC_MESSAGES/django.po
@@ -1,21 +1,18 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-02-14 13:16+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: tests/base.py:101
+#: tests/base.py:100
msgid "lazy message"
-msgstr "lazy message"
+msgstr ""
diff --git a/django/contrib/redirects/locale/en/LC_MESSAGES/django.mo b/django/contrib/redirects/locale/en/LC_MESSAGES/django.mo
index bf854ed382..08a7b68596 100644
Binary files a/django/contrib/redirects/locale/en/LC_MESSAGES/django.mo and b/django/contrib/redirects/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/redirects/locale/en/LC_MESSAGES/django.po b/django/contrib/redirects/locale/en/LC_MESSAGES/django.po
index a0723f0b39..10eb14e213 100644
--- a/django/contrib/redirects/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/redirects/locale/en/LC_MESSAGES/django.po
@@ -1,49 +1,42 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-02-14 13:39+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: models.py:9
msgid "redirect from"
-msgstr "redirect from"
+msgstr ""
#: models.py:10
msgid ""
"This should be an absolute path, excluding the domain name. Example: '/"
"events/search/'."
msgstr ""
-"This should be an absolute path, excluding the domain name. Example: '/"
-"events/search/'."
#: models.py:11
msgid "redirect to"
-msgstr "redirect to"
+msgstr ""
#: models.py:12
msgid ""
"This can be either an absolute path (as above) or a full URL starting with "
"'http://'."
msgstr ""
-"This can be either an absolute path (as above) or a full URL starting with "
-"'http://'."
#: models.py:15
msgid "redirect"
-msgstr "redirect"
+msgstr ""
#: models.py:16
msgid "redirects"
-msgstr "redirects"
+msgstr ""
diff --git a/django/contrib/sessions/locale/en/LC_MESSAGES/django.mo b/django/contrib/sessions/locale/en/LC_MESSAGES/django.mo
index 1f590af580..08a7b68596 100644
Binary files a/django/contrib/sessions/locale/en/LC_MESSAGES/django.mo and b/django/contrib/sessions/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/sessions/locale/en/LC_MESSAGES/django.po b/django/contrib/sessions/locale/en/LC_MESSAGES/django.po
index 09dd449811..247efec01b 100644
--- a/django/contrib/sessions/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/sessions/locale/en/LC_MESSAGES/django.po
@@ -1,37 +1,34 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-02-14 13:40+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: models.py:38
msgid "session key"
-msgstr "session key"
+msgstr ""
#: models.py:40
msgid "session data"
-msgstr "session data"
+msgstr ""
#: models.py:41
msgid "expire date"
-msgstr "expire date"
+msgstr ""
#: models.py:46
msgid "session"
-msgstr "session"
+msgstr ""
#: models.py:47
msgid "sessions"
-msgstr "sessions"
+msgstr ""
diff --git a/django/contrib/sitemaps/tests/test_http.py b/django/contrib/sitemaps/tests/test_http.py
index 1a91d970f3..a99025e6e2 100644
--- a/django/contrib/sitemaps/tests/test_http.py
+++ b/django/contrib/sitemaps/tests/test_http.py
@@ -144,3 +144,10 @@ class HTTPSitemapTests(SitemapTestsBase):
""" % self.base_url
self.assertXMLEqual(response.content.decode('utf-8'), expected_content)
+
+ def test_x_robots_sitemap(self):
+ response = self.client.get('/simple/index.xml')
+ self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive')
+
+ response = self.client.get('/simple/sitemap.xml')
+ self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive')
diff --git a/django/contrib/sitemaps/views.py b/django/contrib/sitemaps/views.py
index c8d2f4dfa0..95bc7eac54 100644
--- a/django/contrib/sitemaps/views.py
+++ b/django/contrib/sitemaps/views.py
@@ -1,4 +1,5 @@
import warnings
+from functools import wraps
from django.contrib.sites.models import get_current_site
from django.core import urlresolvers
@@ -7,6 +8,15 @@ from django.http import Http404
from django.template.response import TemplateResponse
from django.utils import six
+def x_robots_tag(func):
+ @wraps(func)
+ def inner(request, *args, **kwargs):
+ response = func(request, *args, **kwargs)
+ response['X-Robots-Tag'] = 'noindex, noodp, noarchive'
+ return response
+ return inner
+
+@x_robots_tag
def index(request, sitemaps,
template_name='sitemap_index.xml', content_type='application/xml',
sitemap_url_name='django.contrib.sitemaps.views.sitemap',
@@ -35,6 +45,7 @@ def index(request, sitemaps,
return TemplateResponse(request, template_name, {'sitemaps': sites},
content_type=content_type)
+@x_robots_tag
def sitemap(request, sitemaps, section=None,
template_name='sitemap.xml', content_type='application/xml',
mimetype=None):
diff --git a/django/contrib/sites/locale/en/LC_MESSAGES/django.mo b/django/contrib/sites/locale/en/LC_MESSAGES/django.mo
index f8612702dc..08a7b68596 100644
Binary files a/django/contrib/sites/locale/en/LC_MESSAGES/django.mo and b/django/contrib/sites/locale/en/LC_MESSAGES/django.mo differ
diff --git a/django/contrib/sites/locale/en/LC_MESSAGES/django.po b/django/contrib/sites/locale/en/LC_MESSAGES/django.po
index 7d988a9140..c84cad01c8 100644
--- a/django/contrib/sites/locale/en/LC_MESSAGES/django.po
+++ b/django/contrib/sites/locale/en/LC_MESSAGES/django.po
@@ -1,20 +1,17 @@
# This file is distributed under the same license as the Django package.
#
-# Translators:
-# Jannis Leidel , 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-28 10:04+0100\n"
-"PO-Revision-Date: 2012-02-14 13:49+0000\n"
-"Last-Translator: Jannis Leidel \n"
+"POT-Creation-Date: 2013-05-02 16:18+0200\n"
+"PO-Revision-Date: 2010-05-13 15:35+0200\n"
+"Last-Translator: Django team\n"
"Language-Team: English \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: models.py:25
msgid "The domain name cannot contain any spaces or tabs."
@@ -22,16 +19,16 @@ msgstr ""
#: models.py:58
msgid "domain name"
-msgstr "domain name"
+msgstr ""
#: models.py:60
msgid "display name"
-msgstr "display name"
+msgstr ""
#: models.py:65
msgid "site"
-msgstr "site"
+msgstr ""
#: models.py:66
msgid "sites"
-msgstr "sites"
+msgstr ""
diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py
index 6116f31efc..22fdac7c76 100644
--- a/django/contrib/staticfiles/management/commands/collectstatic.py
+++ b/django/contrib/staticfiles/management/commands/collectstatic.py
@@ -116,6 +116,12 @@ class Command(NoArgsCommand):
processor = self.storage.post_process(found_files,
dry_run=self.dry_run)
for original_path, processed_path, processed in processor:
+ if isinstance(processed, Exception):
+ self.stderr.write("Post-processing '%s' failed!" % original_path)
+ # Add a blank line before the traceback, otherwise it's
+ # too easy to miss the relevant part of the error message.
+ self.stderr.write("")
+ raise processed
if processed:
self.log("Post-processed '%s' as '%s'" %
(original_path, processed_path), level=1)
diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py
index f444e12c19..d085cf723f 100644
--- a/django/contrib/staticfiles/storage.py
+++ b/django/contrib/staticfiles/storage.py
@@ -251,7 +251,10 @@ class CachedFilesMixin(object):
for patterns in self._patterns.values():
for pattern, template in patterns:
converter = self.url_converter(name, template)
- content = pattern.sub(converter, content)
+ try:
+ content = pattern.sub(converter, content)
+ except ValueError as exc:
+ yield name, None, exc
if hashed_file_exists:
self.delete(hashed_name)
# then save the processed result
diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
index 900ea8e6b7..acc74db6f5 100644
--- a/django/core/handlers/base.py
+++ b/django/core/handlers/base.py
@@ -237,7 +237,7 @@ def get_path_info(environ):
"""
path_info = environ.get('PATH_INFO', str('/'))
# Under Python 3, strings in environ are decoded with ISO-8859-1;
- # re-encode to recover the original bytestring provided by the webserver.
+ # re-encode to recover the original bytestring provided by the web server.
if six.PY3:
path_info = path_info.encode('iso-8859-1')
# It'd be better to implement URI-to-IRI decoding, see #19508.
@@ -266,7 +266,7 @@ def get_script_name(environ):
else:
script_name = environ.get('SCRIPT_NAME', str(''))
# Under Python 3, strings in environ are decoded with ISO-8859-1;
- # re-encode to recover the original bytestring provided by the webserver.
+ # re-encode to recover the original bytestring provided by the web server.
if six.PY3:
script_name = script_name.encode('iso-8859-1')
# It'd be better to implement URI-to-IRI decoding, see #19508.
diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
index 3c88aeac6d..c348c6c8da 100644
--- a/django/core/handlers/wsgi.py
+++ b/django/core/handlers/wsgi.py
@@ -57,6 +57,7 @@ STATUS_CODE_TEXT = {
415: 'UNSUPPORTED MEDIA TYPE',
416: 'REQUESTED RANGE NOT SATISFIABLE',
417: 'EXPECTATION FAILED',
+ 418: "I'M A TEAPOT",
422: 'UNPROCESSABLE ENTITY',
423: 'LOCKED',
424: 'FAILED DEPENDENCY',
diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py
index a7e98173ac..bc171176c2 100644
--- a/django/core/management/commands/makemessages.py
+++ b/django/core/management/commands/makemessages.py
@@ -294,7 +294,10 @@ class Command(NoArgsCommand):
os.unlink(potfile)
for f in file_list:
- f.process(self, potfile, self.domain, self.keep_pot)
+ try:
+ f.process(self, potfile, self.domain, self.keep_pot)
+ except UnicodeDecodeError:
+ self.stdout.write("UnicodeDecodeError: skipped file %s in %s" % (f.file, f.dirpath))
return potfile
def find_files(self, root):
diff --git a/django/core/management/templates.py b/django/core/management/templates.py
index 0f45399109..893e5c95af 100644
--- a/django/core/management/templates.py
+++ b/django/core/management/templates.py
@@ -43,7 +43,7 @@ class TemplateCommand(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--template',
action='store', dest='template',
- help='The dotted import path to load the template from.'),
+ help='The path or URL to load the template from.'),
make_option('--extension', '-e', dest='extensions',
action='append', default=['py'],
help='The file extension(s) to render (default: "py"). '
diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py
index d329221ce4..387da6b3cd 100644
--- a/django/core/servers/basehttp.py
+++ b/django/core/servers/basehttp.py
@@ -108,6 +108,8 @@ class ServerHandler(simple_server.ServerHandler, object):
class WSGIServer(simple_server.WSGIServer, object):
"""BaseHTTPServer that implements the Python WSGI protocol"""
+ request_queue_size = 10
+
def __init__(self, *args, **kwargs):
if kwargs.pop('ipv6', False):
self.address_family = socket.AF_INET6
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index 90f5018f8b..fd9655077e 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -30,6 +30,11 @@ if (version < (1, 2, 1) or (version[:3] == (1, 2, 1) and
from MySQLdb.converters import conversions, Thing2Literal
from MySQLdb.constants import FIELD_TYPE, CLIENT
+try:
+ import pytz
+except ImportError:
+ pytz = None
+
from django.conf import settings
from django.db import utils
from django.db.backends import *
@@ -123,7 +128,7 @@ class CursorWrapper(object):
except Database.OperationalError as e:
# Map some error codes to IntegrityError, since they seem to be
# misclassified and Django would prefer the more logical place.
- if e[0] in self.codes_for_integrityerror:
+ if e.args[0] in self.codes_for_integrityerror:
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
raise
@@ -133,7 +138,7 @@ class CursorWrapper(object):
except Database.OperationalError as e:
# Map some error codes to IntegrityError, since they seem to be
# misclassified and Django would prefer the more logical place.
- if e[0] in self.codes_for_integrityerror:
+ if e.args[0] in self.codes_for_integrityerror:
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
raise
@@ -190,6 +195,15 @@ class DatabaseFeatures(BaseDatabaseFeatures):
@cached_property
def has_zoneinfo_database(self):
+ # MySQL accepts full time zones names (eg. Africa/Nairobi) but rejects
+ # abbreviations (eg. EAT). When pytz isn't installed and the current
+ # time zone is LocalTimezone (the only sensible value in this
+ # context), the current time zone name will be an abbreviation. As a
+ # consequence, MySQL cannot perform time zone conversions reliably.
+ if pytz is None:
+ return False
+
+ # Test if the time zone definitions are installed.
cursor = self.connection.cursor()
cursor.execute("SELECT 1 FROM mysql.time_zone LIMIT 1")
return cursor.fetchone() is not None
@@ -395,8 +409,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
kwargs = {
'conv': django_conversions,
'charset': 'utf8',
- 'use_unicode': True,
}
+ if not six.PY3:
+ kwargs['use_unicode'] = True
settings_dict = self.settings_dict
if settings_dict['USER']:
kwargs['user'] = settings_dict['USER']
diff --git a/django/db/backends/mysql/validation.py b/django/db/backends/mysql/validation.py
index de7474d1e5..2ce957cce7 100644
--- a/django/db/backends/mysql/validation.py
+++ b/django/db/backends/mysql/validation.py
@@ -10,6 +10,7 @@ class DatabaseValidation(BaseDatabaseValidation):
from django.db import models
varchar_fields = (models.CharField, models.CommaSeparatedIntegerField,
models.SlugField)
- if isinstance(f, varchar_fields) and f.max_length > 255 and f.unique:
+ if (isinstance(f, varchar_fields) and f.unique
+ and (f.max_length is None or int(f.max_length) > 255)):
msg = '"%(name)s": %(cls)s cannot have a "max_length" greater than 255 when using "unique=True".'
errors.add(opts, msg % {'name': f.name, 'cls': f.__class__.__name__})
diff --git a/django/db/models/__init__.py b/django/db/models/__init__.py
index 6c5ccd4bd2..5f17229753 100644
--- a/django/db/models/__init__.py
+++ b/django/db/models/__init__.py
@@ -1,3 +1,5 @@
+from functools import wraps
+
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models
from django.db.models.query import Q
@@ -11,7 +13,6 @@ from django.db.models.fields.files import FileField, ImageField
from django.db.models.fields.related import ForeignKey, ForeignObject, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel
from django.db.models.deletion import CASCADE, PROTECT, SET, SET_NULL, SET_DEFAULT, DO_NOTHING, ProtectedError
from django.db.models import signals
-from django.utils.decorators import wraps
def permalink(func):
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index c30ba03489..37fa8b1027 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -204,9 +204,6 @@ class SingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjectDescri
return rel_obj
def __set__(self, instance, value):
- if instance is None:
- raise AttributeError("%s must be accessed via instance" % self.related.opts.object_name)
-
# The similarity of the code below to the code in
# ReverseSingleRelatedObjectDescriptor is annoying, but there's a bunch
# of small differences that would make a common base class convoluted.
@@ -310,9 +307,6 @@ class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjec
return rel_obj
def __set__(self, instance, value):
- if instance is None:
- raise AttributeError("%s must be accessed via instance" % self.field.name)
-
# If null=True, we can assign null here, but otherwise the value needs
# to be an instance of the related class.
if value is None and self.field.null == False:
@@ -382,9 +376,6 @@ class ForeignRelatedObjectsDescriptor(object):
return self.related_manager_cls(instance)
def __set__(self, instance, value):
- if instance is None:
- raise AttributeError("Manager must be accessed via instance")
-
manager = self.__get__(instance)
# If the foreign key can support nulls, then completely clear the related set.
# Otherwise, just move the named objects into the set.
@@ -765,9 +756,6 @@ class ManyRelatedObjectsDescriptor(object):
return manager
def __set__(self, instance, value):
- if instance is None:
- raise AttributeError("Manager must be accessed via instance")
-
if not self.related.field.rel.through._meta.auto_created:
opts = self.related.field.rel.through._meta
raise AttributeError("Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s.%s's Manager instead." % (opts.app_label, opts.object_name))
@@ -822,9 +810,6 @@ class ReverseManyRelatedObjectsDescriptor(object):
return manager
def __set__(self, instance, value):
- if instance is None:
- raise AttributeError("Manager must be accessed via instance")
-
if not self.field.rel.through._meta.auto_created:
opts = self.field.rel.through._meta
raise AttributeError("Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s.%s's Manager instead." % (opts.app_label, opts.object_name))
diff --git a/django/db/models/query.py b/django/db/models/query.py
index c36d81cc31..337049e2ff 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -312,11 +312,7 @@ class QuerySet(object):
if skip:
obj = model_cls(**dict(zip(init_list, row_data)))
else:
- try:
- obj = model(*row_data)
- except IndexError:
- import ipdb; ipdb.set_trace()
- pass
+ obj = model(*row_data)
# Store the source database of the object
obj._state.db = db
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index a33c44833c..ee7a56a26c 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -102,7 +102,7 @@ class DeferredAttribute(object):
f = [f for f in opts.fields
if f.attname == self.field_name][0]
name = f.name
- # Lets see if the field is part of the parent chain. If so we
+ # Let's see if the field is part of the parent chain. If so we
# might be able to reuse the already loaded value. Refs #18343.
val = self._check_parent_chain(instance, name)
if val is None:
@@ -194,8 +194,7 @@ def deferred_class_factory(model, attrs):
name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs))))
name = util.truncate_name(name, 80, 32)
- overrides = dict([(attr, DeferredAttribute(attr, model))
- for attr in attrs])
+ overrides = dict((attr, DeferredAttribute(attr, model)) for attr in attrs)
overrides["Meta"] = Meta
overrides["__module__"] = model.__module__
overrides["_deferred"] = True
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 3444b74ac3..018fc098ea 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -1092,7 +1092,7 @@ class SQLDateTimeCompiler(SQLCompiler):
if datetime is None:
raise ValueError("Database returned an invalid value "
"in QuerySet.dates(). Are time zone "
- "definitions installed?")
+ "definitions and pytz installed?")
datetime = datetime.replace(tzinfo=None)
datetime = timezone.make_aware(datetime, self.query.tzinfo)
yield datetime
diff --git a/django/db/utils.py b/django/db/utils.py
index 936b42039d..e84060f9b3 100644
--- a/django/db/utils.py
+++ b/django/db/utils.py
@@ -169,7 +169,7 @@ class ConnectionHandler(object):
conn.setdefault('ENGINE', 'django.db.backends.dummy')
if conn['ENGINE'] == 'django.db.backends.' or not conn['ENGINE']:
conn['ENGINE'] = 'django.db.backends.dummy'
- conn.setdefault('CONN_MAX_AGE', 600)
+ conn.setdefault('CONN_MAX_AGE', 0)
conn.setdefault('OPTIONS', {})
conn.setdefault('TIME_ZONE', 'UTC' if settings.USE_TZ else settings.TIME_ZONE)
for setting in ['NAME', 'USER', 'PASSWORD', 'HOST', 'PORT']:
diff --git a/django/forms/formsets.py b/django/forms/formsets.py
index 2ab197dee2..2bd11d9f53 100644
--- a/django/forms/formsets.py
+++ b/django/forms/formsets.py
@@ -9,7 +9,7 @@ from django.utils.encoding import python_2_unicode_compatible
from django.utils.safestring import mark_safe
from django.utils import six
from django.utils.six.moves import xrange
-from django.utils.translation import ugettext as _
+from django.utils.translation import ungettext, ugettext as _
__all__ = ('BaseFormSet', 'all_valid')
@@ -302,7 +302,9 @@ class BaseFormSet(object):
try:
if (self.validate_max and self.total_form_count() > self.max_num) or \
self.management_form.cleaned_data[TOTAL_FORM_COUNT] > self.absolute_max:
- raise ValidationError(_("Please submit %s or fewer forms." % self.max_num))
+ raise ValidationError(ungettext(
+ "Please submit %d or fewer forms.",
+ "Please submit %d or fewer forms.", self.max_num) % self.max_num)
# Give self.clean() a chance to do cross-form validation.
self.clean()
except ValidationError as e:
diff --git a/django/forms/models.py b/django/forms/models.py
index 5e7797809a..af5cda8faf 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -5,6 +5,8 @@ and database field objects.
from __future__ import absolute_import, unicode_literals
+import warnings
+
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError
from django.forms.fields import Field, ChoiceField
from django.forms.forms import BaseForm, get_declared_fields
@@ -22,8 +24,12 @@ from django.utils.translation import ugettext_lazy as _, ugettext
__all__ = (
'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
'save_instance', 'ModelChoiceField', 'ModelMultipleChoiceField',
+ 'ALL_FIELDS',
)
+ALL_FIELDS = '__all__'
+
+
def construct_instance(form, instance, fields=None, exclude=None):
"""
Constructs and returns a model instance from the bound ``form``'s
@@ -211,7 +217,7 @@ class ModelFormMetaclass(type):
# of ('foo',)
for opt in ['fields', 'exclude']:
value = getattr(opts, opt)
- if isinstance(value, six.string_types):
+ if isinstance(value, six.string_types) and value != ALL_FIELDS:
msg = ("%(model)s.Meta.%(opt)s cannot be a string. "
"Did you mean to type: ('%(value)s',)?" % {
'model': new_class.__name__,
@@ -222,6 +228,20 @@ class ModelFormMetaclass(type):
if opts.model:
# If a model is defined, extract form fields from it.
+
+ if opts.fields is None and opts.exclude is None:
+ # This should be some kind of assertion error once deprecation
+ # cycle is complete.
+ warnings.warn("Creating a ModelForm without either the 'fields' attribute "
+ "or the 'exclude' attribute is deprecated - form %s "
+ "needs updating" % name,
+ PendingDeprecationWarning)
+
+ if opts.fields == ALL_FIELDS:
+ # sentinel for fields_for_model to indicate "get the list of
+ # fields from the model"
+ opts.fields = None
+
fields = fields_for_model(opts.model, opts.fields,
opts.exclude, opts.widgets, formfield_callback)
# make sure opts.fields doesn't specify an invalid field
@@ -394,7 +414,8 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
Returns a ModelForm containing form fields for the given model.
``fields`` is an optional list of field names. If provided, only the named
- fields will be included in the returned fields.
+ fields will be included in the returned fields. If omitted or '__all__',
+ all fields will be used.
``exclude`` is an optional list of field names. If provided, the named
fields will be excluded from the returned fields, even if they are listed
@@ -434,6 +455,15 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
'formfield_callback': formfield_callback
}
+ # The ModelFormMetaclass will trigger a similar warning/error, but this will
+ # be difficult to debug for code that needs updating, so we produce the
+ # warning here too.
+ if (getattr(Meta, 'fields', None) is None and
+ getattr(Meta, 'exclude', None) is None):
+ warnings.warn("Calling modelform_factory without defining 'fields' or "
+ "'exclude' explicitly is deprecated",
+ PendingDeprecationWarning, stacklevel=2)
+
# Instatiate type(form) in order to use the same metaclass as form.
return type(form)(class_name, (form,), form_class_attrs)
@@ -701,6 +731,21 @@ def modelformset_factory(model, form=ModelForm, formfield_callback=None,
"""
Returns a FormSet class for the given Django model class.
"""
+ # modelform_factory will produce the same warning/error, but that will be
+ # difficult to debug for code that needs upgrading, so we produce the
+ # warning here too. This logic is reproducing logic inside
+ # modelform_factory, but it can be removed once the deprecation cycle is
+ # complete, since the validation exception will produce a helpful
+ # stacktrace.
+ meta = getattr(form, 'Meta', None)
+ if meta is None:
+ meta = type(str('Meta'), (object,), {})
+ if (getattr(meta, 'fields', fields) is None and
+ getattr(meta, 'exclude', exclude) is None):
+ warnings.warn("Calling modelformset_factory without defining 'fields' or "
+ "'exclude' explicitly is deprecated",
+ PendingDeprecationWarning, stacklevel=2)
+
form = modelform_factory(model, form=form, fields=fields, exclude=exclude,
formfield_callback=formfield_callback,
widgets=widgets)
@@ -1091,3 +1136,11 @@ class ModelMultipleChoiceField(ModelChoiceField):
initial_set = set([force_text(value) for value in self.prepare_value(initial)])
data_set = set([force_text(value) for value in data])
return data_set != initial_set
+
+
+def modelform_defines_fields(form_class):
+ return (form_class is not None and (
+ hasattr(form_class, '_meta') and
+ (form_class._meta.fields is not None or
+ form_class._meta.exclude is not None)
+ ))
diff --git a/django/test/utils.py b/django/test/utils.py
index d839c0403c..92cef59f72 100644
--- a/django/test/utils.py
+++ b/django/test/utils.py
@@ -1,5 +1,6 @@
import re
import warnings
+from functools import wraps
from xml.dom.minidom import parseString, Node
from django.conf import settings, UserSettingsHolder
@@ -10,7 +11,6 @@ from django.template import Template, loader, TemplateDoesNotExist
from django.template.loaders import cached
from django.test.signals import template_rendered, setting_changed
from django.utils.encoding import force_str
-from django.utils.functional import wraps
from django.utils import six
from django.utils.translation import deactivate
diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py
index ec68892870..b0b1cd6b1d 100644
--- a/django/utils/datastructures.py
+++ b/django/utils/datastructures.py
@@ -14,13 +14,19 @@ class MergeDict(object):
def __init__(self, *dicts):
self.dicts = dicts
+ def __bool__(self):
+ return any(self.dicts)
+
+ def __nonzero__(self):
+ return type(self).__bool__(self)
+
def __getitem__(self, key):
for dict_ in self.dicts:
try:
return dict_[key]
except KeyError:
pass
- raise KeyError
+ raise KeyError(key)
def __copy__(self):
return self.__class__(*self.dicts)
diff --git a/django/utils/functional.py b/django/utils/functional.py
index 13aec9cb82..1592828c7f 100644
--- a/django/utils/functional.py
+++ b/django/utils/functional.py
@@ -346,6 +346,7 @@ class SimpleLazyObject(LazyObject):
# care about this (especially in equality tests)
__class__ = property(new_method_proxy(operator.attrgetter("__class__")))
__eq__ = new_method_proxy(operator.eq)
+ __ne__ = new_method_proxy(operator.ne)
__hash__ = new_method_proxy(hash)
__bool__ = new_method_proxy(bool) # Python 3
__nonzero__ = __bool__ # Python 2
diff --git a/django/views/generic/edit.py b/django/views/generic/edit.py
index 5b97fc81c9..e2cc741ffb 100644
--- a/django/views/generic/edit.py
+++ b/django/views/generic/edit.py
@@ -1,3 +1,5 @@
+import warnings
+
from django.forms import models as model_forms
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponseRedirect
@@ -95,7 +97,14 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
# Try to get a queryset and extract the model class
# from that
model = self.get_queryset().model
- return model_forms.modelform_factory(model)
+
+ fields = getattr(self, 'fields', None)
+ if fields is None:
+ warnings.warn("Using ModelFormMixin (base class of %s) without "
+ "the 'fields' attribute is deprecated." % self.__class__.__name__,
+ PendingDeprecationWarning)
+
+ return model_forms.modelform_factory(model, fields=fields)
def get_form_kwargs(self):
"""
diff --git a/django/views/i18n.py b/django/views/i18n.py
index c7ee1b33bd..37ec10b552 100644
--- a/django/views/i18n.py
+++ b/django/views/i18n.py
@@ -6,7 +6,7 @@ from django import http
from django.conf import settings
from django.template import Context, Template
from django.utils import importlib
-from django.utils.translation import check_for_language, activate, to_locale, get_language
+from django.utils.translation import check_for_language, to_locale, get_language
from django.utils.encoding import smart_text
from django.utils.formats import get_format_modules, get_format
from django.utils._os import upath
@@ -205,17 +205,18 @@ def javascript_catalog(request, domain='djangojs', packages=None):
go to the djangojs domain. But this might be needed if you
deliver your JavaScript source from Django templates.
"""
- if request.GET:
- if 'language' in request.GET:
- if check_for_language(request.GET['language']):
- activate(request.GET['language'])
+ default_locale = to_locale(settings.LANGUAGE_CODE)
+ locale = to_locale(get_language())
+
+ if request.GET and 'language' in request.GET:
+ if check_for_language(request.GET['language']):
+ locale = to_locale(request.GET['language'])
+
if packages is None:
packages = ['django.conf']
if isinstance(packages, six.string_types):
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)
- locale = to_locale(get_language())
t = {}
paths = []
en_selected = locale.startswith('en')
diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py
index 572bcd2e29..3ae770da20 100644
--- a/docs/_ext/djangodocs.py
+++ b/docs/_ext/djangodocs.py
@@ -5,9 +5,7 @@ import json
import os
import re
-from docutils import nodes, transforms
-
-from sphinx import addnodes, roles, __version__ as sphinx_ver
+from sphinx import addnodes, __version__ as sphinx_ver
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.writers.html import SmartyPantsHTMLTranslator
from sphinx.util.console import bold
@@ -64,21 +62,25 @@ class VersionDirective(Directive):
option_spec = {}
def run(self):
+ if len(self.arguments) > 1:
+ msg = """Only one argument accepted for directive '{directive_name}::'.
+ Comments should be provided as content,
+ not as an extra argument.""".format(directive_name=self.name)
+ raise self.error(msg)
+
env = self.state.document.settings.env
ret = []
node = addnodes.versionmodified()
ret.append(node)
+
if self.arguments[0] == env.config.django_next_version:
node['version'] = "Development version"
else:
node['version'] = self.arguments[0]
+
node['type'] = self.name
- if len(self.arguments) == 2:
- inodes, messages = self.state.inline_text(self.arguments[1], self.lineno+1)
- node.extend(inodes)
- if self.content:
- self.state.nested_parse(self.content, self.content_offset, node)
- ret = ret + messages
+ if self.content:
+ self.state.nested_parse(self.content, self.content_offset, node)
env.note_versionchange(node['type'], node['version'], node, self.lineno)
return ret
diff --git a/docs/howto/custom-management-commands.txt b/docs/howto/custom-management-commands.txt
index 7a31fc44e3..34e68d3700 100644
--- a/docs/howto/custom-management-commands.txt
+++ b/docs/howto/custom-management-commands.txt
@@ -256,7 +256,7 @@ All attributes can be set in your derived class and can be used in
.. versionadded:: 1.6
-The ``leave_locale_alone`` option was added in Django 1.6.
+ The ``leave_locale_alone`` option was added in Django 1.6.
Methods
-------
diff --git a/docs/howto/deployment/checklist.txt b/docs/howto/deployment/checklist.txt
index b092048870..b72be75497 100644
--- a/docs/howto/deployment/checklist.txt
+++ b/docs/howto/deployment/checklist.txt
@@ -157,6 +157,15 @@ Performance optimizations
Setting :setting:`DEBUG = False ` disables several features that are
only useful in development. In addition, you can tune the following settings.
+:setting:`CONN_MAX_AGE`
+-----------------------
+
+Enabling `persistent database connections `_
+can result in a nice speed-up when connecting to the database accounts for a
+significant part of the request processing time.
+
+This helps a lot on virtualized hosts with limited network performance.
+
:setting:`TEMPLATE_LOADERS`
---------------------------
diff --git a/docs/howto/deployment/fastcgi.txt b/docs/howto/deployment/fastcgi.txt
index 6a5acfb7cc..507e50d1a2 100644
--- a/docs/howto/deployment/fastcgi.txt
+++ b/docs/howto/deployment/fastcgi.txt
@@ -112,7 +112,7 @@ Running a preforked server on a Unix domain socket::
.. admonition:: Socket security
- Django's default umask requires that the webserver and the Django fastcgi
+ Django's default umask requires that the web server and the Django fastcgi
process be run with the same group **and** user. For increased security,
you can run them under the same group but as different users. If you do
this, you will need to set the umask to 0002 using the ``umask`` argument
diff --git a/docs/howto/legacy-databases.txt b/docs/howto/legacy-databases.txt
index 6846e4b2df..0bea8b41c4 100644
--- a/docs/howto/legacy-databases.txt
+++ b/docs/howto/legacy-databases.txt
@@ -61,7 +61,7 @@ this generated model definition:
class Person(models.Model):
id = models.IntegerField(primary_key=True)
- first_name = models.ChaField(max_length=70)
+ first_name = models.CharField(max_length=70)
class Meta:
**managed = False**
db_table = 'CENSUS_PERSONS'
@@ -75,8 +75,8 @@ access to your precious data on a model by model basis.
.. versionchanged:: 1.6
-The behavior by which introspected models are created as unmanaged ones is new
-in Django 1.6.
+ The behavior by which introspected models are created as unmanaged ones is new
+ in Django 1.6.
Install the core Django tables
==============================
diff --git a/docs/howto/static-files/index.txt b/docs/howto/static-files/index.txt
index 2c98566e88..1fdad94143 100644
--- a/docs/howto/static-files/index.txt
+++ b/docs/howto/static-files/index.txt
@@ -32,7 +32,7 @@ Configuring static files
{% load staticfiles %}
-3. Store your static files in a folder called ``static`` in your app. For
+4. Store your static files in a folder called ``static`` in your app. For
example ``my_app/static/my_app/myimage.jpg``.
Now, if you use ``./manage.py runserver``, all static files should be served
@@ -106,7 +106,7 @@ for gathering static files in a single directory so you can serve them easily.
This will copy all files from your static folders into the
:setting:`STATIC_ROOT` directory.
-3. Use a webserver of your choice to serve the
+3. Use a web server of your choice to serve the
files. :doc:`/howto/static-files/deployment` covers some common deployment
strategies for static files.
diff --git a/docs/internals/committers.txt b/docs/internals/committers.txt
index a0649f38a2..f891bc4eb7 100644
--- a/docs/internals/committers.txt
+++ b/docs/internals/committers.txt
@@ -15,7 +15,7 @@ Journal-World`_ of Lawrence, Kansas, USA.
He was lead developer at World Online for 2.5 years, during which time
Django was developed and implemented on World Online's sites. He was the
- leader and founder of EveryBlock_, a "news feed for your block." He now
+ leader and founder of EveryBlock_, a "news feed for your block." He now
develops for Soundslice_.
Adrian lives in Chicago, USA.
@@ -41,7 +41,7 @@ Journal-World`_ of Lawrence, Kansas, USA.
`Wilson Miner`_
Wilson's design-fu is what makes Django look so nice. He designed the
Web site you're looking at right now, as well as Django's acclaimed admin
- interface. Wilson was the designer for EveryBlock and Rdio_. He now
+ interface. Wilson was the designer for EveryBlock and Rdio_. He now
designs for Facebook.
Wilson lives in San Francisco, USA.
@@ -105,8 +105,8 @@ Malcolm Tredinnick
.. _russell keith-magee: http://cecinestpasun.com/
Joseph Kocherhans
- Joseph was the director of lead development at EveryBlock and previously
- developed at the Lawrence Journal-World. He is treasurer of the `Django
+ Joseph was the director of lead development at EveryBlock and previously
+ developed at the Lawrence Journal-World. He is treasurer of the `Django
Software Foundation`_. He often disappears for several days into the woods,
attempts to teach himself computational linguistics, and annoys his
neighbors with his Charango_ playing.
@@ -386,17 +386,17 @@ Florian Apolloner
.. _Ubuntuusers webteam: http://wiki.ubuntuusers.de/ubuntuusers/Webteam
Jeremy Dunck
- Jeremy was rescued from corporate IT drudgery by Free Software and, in part,
+ Jeremy was rescued from corporate IT drudgery by Free Software and, in part,
Django. Many of Jeremy's interests center around access to information.
- Jeremy was the lead developer of Pegasus News, one of the first uses of
- Django outside World Online, and has since joined Votizen, a startup intent
+ Jeremy was the lead developer of Pegasus News, one of the first uses of
+ Django outside World Online, and has since joined Votizen, a startup intent
on reducing the influence of money in politics.
- He serves as DSF Secretary, organizes and helps organize sprints, cares
- about the health and equity of the Django community. He has gone an
+ He serves as DSF Secretary, organizes and helps organize sprints, cares
+ about the health and equity of the Django community. He has gone an
embarrassingly long time without a working blog.
-
+
Jeremy lives in Mountain View, CA, USA.
`Bryan Veloso`_
@@ -441,6 +441,20 @@ Jeremy Dunck
.. _Ultimate Frisbee: http://www.montrealultimate.ca
.. _Reptiletech: http://www.reptiletech.com
+`Daniel Lindsley`_
+ Pythonista since 2003, Djangonaut since 2006. Daniel started with Django
+ just after the v0.90 release (back when ``Manipulators`` looked good) & fell
+ in love. Since then, he wrote third-party apps like Haystack & Tastypie
+ & has run the annual Django Dash since 2007. One of the testing faithful,
+ Daniel's contributions include rewriting the ``Forms`` test suite & the
+ addition of ``request.is_ajax``. Daniel currently works as a Python
+ developer at `Amazon Web Services`_ on the ``boto`` library.
+
+ Daniel lives in Seattle, WA, USA.
+
+.. _`Daniel Lindsley`: http://toastdriven.com/
+.. _`Amazon Web Services`: https://aws.amazon.com/
+
Specialists
-----------
diff --git a/docs/internals/contributing/localizing.txt b/docs/internals/contributing/localizing.txt
index 0cde77882c..01b88d8d9a 100644
--- a/docs/internals/contributing/localizing.txt
+++ b/docs/internals/contributing/localizing.txt
@@ -62,5 +62,5 @@ Django source tree, as for any code change:
.. _Transifex: https://www.transifex.com/
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
-.. _Django project page: https://www.transifex.com/projects/p/django/
+.. _Django project page: https://www.transifex.com/projects/p/django-core/
.. _Transifex User Guide: http://help.transifex.com/
diff --git a/docs/internals/contributing/writing-documentation.txt b/docs/internals/contributing/writing-documentation.txt
index 469f8614b9..2944dea504 100644
--- a/docs/internals/contributing/writing-documentation.txt
+++ b/docs/internals/contributing/writing-documentation.txt
@@ -188,8 +188,8 @@ Our policy for new features is:
release, not the development version.
Our preferred way for marking new features is by prefacing the features'
-documentation with: "``.. versionadded:: X.Y``", followed by an optional one
-line comment and a mandatory blank line.
+documentation with: "``.. versionadded:: X.Y``", followed by a a mandatory
+blank line and an optional content (indented).
General improvements, or other changes to the APIs that should be emphasized
should use the "``.. versionchanged:: X.Y``" directive (with the same format
diff --git a/docs/internals/howto-release-django.txt b/docs/internals/howto-release-django.txt
index a49251da76..fd985ddafc 100644
--- a/docs/internals/howto-release-django.txt
+++ b/docs/internals/howto-release-django.txt
@@ -175,13 +175,13 @@ OK, this is the fun part, where we actually push out a release!
#. Make sure you have an absolutely clean tree by running ``git clean -dfx``.
-#. Run ``python setup.py sdist`` to generate the release package. This will
- create the release package in a ``dist/`` directory.
+#. Run ``make -f extras/Makefile`` to generate the release packages. This will
+ create the release packages in a ``dist/`` directory.
-#. Generate the hashes of the release package::
+#. Generate the hashes of the release packages::
- $ md5sum dist/Django-.tar.gz
- $ sha1sum dist/Django-.tar.gz
+ $ md5sum dist/Django-*
+ $ sha1sum dist/Django-*
*FIXME: perhaps we should switch to sha256?*
@@ -221,6 +221,9 @@ Now you're ready to actually put the release out there. To do this:
$ mktmpenv
$ pip install https://www.djangoproject.com/m/releases/1.5/Django-1.5.1.tar.gz
$ deactivate
+ $ mktmpenv
+ $ pip install https://www.djangoproject.com/m/releases/1.5/Django-1.5.1-py2.py3-none-any.whl
+ $ deactivate
This just tests that the tarballs are available (i.e. redirects are up) and
that they install correctly, but it'll catch silly mistakes.
diff --git a/docs/intro/contributing.txt b/docs/intro/contributing.txt
index 8747375b0a..dd7136c877 100644
--- a/docs/intro/contributing.txt
+++ b/docs/intro/contributing.txt
@@ -389,8 +389,8 @@ This is a new feature, so it should be documented. Add the following on line
.. versionadded:: 1.5
- The current value of the field will be displayed as a clickable link above the
- input widget.
+ The current value of the field will be displayed as a clickable link above the
+ input widget.
For more information on writing documentation, including an explanation of what
the ``versionadded`` bit is all about, see
diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt
index 1e0f1da0c5..4247b45238 100644
--- a/docs/intro/reusable-apps.txt
+++ b/docs/intro/reusable-apps.txt
@@ -66,16 +66,17 @@ After the previous tutorials, our project should look like this::
__init__.py
admin.py
models.py
- tests.py
static/
- style.css
- images/
- background.gif
+ polls
+ images/
+ background.gif
+ style.css
templates/
polls/
detail.html
index.html
results.html
+ tests.py
urls.py
views.py
templates/
@@ -129,7 +130,7 @@ this. For a small app like polls, this process isn't too difficult.
2. Move the ``polls`` directory into the ``django-polls`` directory.
-3. Create a file ``django-polls/README.txt`` with the following contents::
+3. Create a file ``django-polls/README.rst`` with the following contents::
=====
Polls
@@ -157,7 +158,7 @@ this. For a small app like polls, this process isn't too difficult.
3. Run `python manage.py syncdb` to create the polls models.
4. Start the development server and visit http://127.0.0.1:8000/admin/
- to create a poll (you'll need the Admin app enabled).
+ to create a poll (you'll need the Admin app enabled).
5. Visit http://127.0.0.1:8000/polls/ to participate in the poll.
@@ -177,23 +178,23 @@ Create a file ``django-polls/setup.py`` with the following contents::
import os
from setuptools import setup
- README = open(os.path.join(os.path.dirname(__file__), 'README.txt')).read()
+ README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read()
# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
setup(
- name = 'django-polls',
- version = '0.1',
- packages = ['polls'],
- include_package_data = True,
- license = 'BSD License', # example license
- description = 'A simple Django app to conduct Web-based polls.',
- long_description = README,
- url = 'http://www.example.com/',
- author = 'Your Name',
- author_email = 'yourname@example.com',
- classifiers = [
+ name='django-polls',
+ version='0.1',
+ packages=['polls'],
+ include_package_data=True,
+ license='BSD License', # example license
+ description='A simple Django app to conduct Web-based polls.',
+ long_description=README,
+ url='http://www.example.com/',
+ author='Your Name',
+ author_email='yourname@example.com',
+ classifiers=[
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
@@ -216,10 +217,13 @@ Create a file ``django-polls/setup.py`` with the following contents::
6. Only Python modules and packages are included in the package by default. To
include additional files, we'll need to create a ``MANIFEST.in`` file. The
distribute docs referred to in the previous step discuss this file in more
- details. To include the templates and our LICENSE file, create a file
- ``django-polls/MANIFEST.in`` with the following contents::
+ details. To include the templates, the ``README.rst`` and our ``LICENSE``
+ file, create a file ``django-polls/MANIFEST.in`` with the following
+ contents::
include LICENSE
+ include README.rst
+ recursive-include polls/static *
recursive-include polls/templates *
7. It's optional, but recommended, to include detailed documentation with your
diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt
index 7f69945300..d623bd8451 100644
--- a/docs/intro/tutorial01.txt
+++ b/docs/intro/tutorial01.txt
@@ -23,7 +23,8 @@ If Django is installed, you should see the version of your installation. If it
isn't, you'll get an error telling "No module named django".
This tutorial is written for Django |version| and Python 2.x. If the Django
-version doesn't match, you can refer to the tutorial for your version of Django
+version doesn't match, you can refer to the tutorial for your version of
+Django by using the version switcher at the bottom right corner of this page,
or update Django to the newest version. If you are using Python 3.x, be aware
that your code may need to differ from what is in the tutorial and you should
continue using the tutorial only if you know what you are doing with Python
@@ -122,7 +123,7 @@ These files are:
"table of contents" of your Django-powered site. You can read more about
URLs in :doc:`/topics/http/urls`.
-* :file:`mysite/wsgi.py`: An entry-point for WSGI-compatible webservers to
+* :file:`mysite/wsgi.py`: An entry-point for WSGI-compatible web servers to
serve your project. See :doc:`/howto/deployment/wsgi/index` for more details.
.. _more about packages: http://docs.python.org/tutorial/modules.html#packages
@@ -242,9 +243,6 @@ come with Django:
* :mod:`django.contrib.sessions` -- A session framework.
-* :mod:`django.contrib.sites` -- A framework for managing multiple sites
- with one Django installation.
-
* :mod:`django.contrib.messages` -- A messaging framework.
* :mod:`django.contrib.staticfiles` -- A framework for managing
@@ -252,7 +250,7 @@ come with Django:
These applications are included by default as a convenience for the common case.
-Each of these applications makes use of at least one database table, though,
+Some of these applications makes use of at least one database table, though,
so we need to create the tables in the database before we can use them. To do
that, run the following command:
@@ -581,27 +579,32 @@ Wait a minute. ```` is, utterly, an unhelpful representation
of this object. Let's fix that by editing the polls model (in the
``polls/models.py`` file) and adding a
:meth:`~django.db.models.Model.__unicode__` method to both ``Poll`` and
-``Choice``::
+``Choice``. On Python 3, simply replace ``__unicode__`` by ``__str__`` in the
+following example::
class Poll(models.Model):
# ...
- def __unicode__(self):
+ def __unicode__(self): # Python 3: def __str__(self):
return self.question
class Choice(models.Model):
# ...
- def __unicode__(self):
+ def __unicode__(self): # Python 3: def __str__(self):
return self.choice_text
-It's important to add :meth:`~django.db.models.Model.__unicode__` methods to
-your models, not only for your own sanity when dealing with the interactive
-prompt, but also because objects' representations are used throughout Django's
-automatically-generated admin.
+It's important to add :meth:`~django.db.models.Model.__unicode__` methods (or
+:meth:`~django.db.models.Model.__str__` on Python 3) to your models, not only
+for your own sanity when dealing with the interactive prompt, but also because
+objects' representations are used throughout Django's automatically-generated
+admin.
-.. admonition:: Why :meth:`~django.db.models.Model.__unicode__` and not
- :meth:`~django.db.models.Model.__str__`?
+.. admonition:: `__unicode__` or `__str__`?
- If you're familiar with Python, you might be in the habit of adding
+ On Python 3, things are simpler, just use
+ :meth:`~django.db.models.Model.__str__` and forget about
+ :meth:`~django.db.models.Model.__unicode__`.
+
+ If you're familiar with Python 2, you might be in the habit of adding
:meth:`~django.db.models.Model.__str__` methods to your classes, not
:meth:`~django.db.models.Model.__unicode__` methods. We use
:meth:`~django.db.models.Model.__unicode__` here because Django models deal
diff --git a/docs/intro/tutorial05.txt b/docs/intro/tutorial05.txt
index 3b0a95f253..97d1d96ad7 100644
--- a/docs/intro/tutorial05.txt
+++ b/docs/intro/tutorial05.txt
@@ -15,9 +15,9 @@ What are automated tests?
Tests are simple routines that check the operation of your code.
Testing operates at different levels. Some tests might apply to a tiny detail
-- *does a particular model method return values as expected?*, while others
-examine the overall operation of the software - *does a sequence of user inputs
-on the site produce the desired result?* That's no different from the kind of
+(*does a particular model method return values as expected?*) while others
+examine the overall operation of the software (*does a sequence of user inputs
+on the site produce the desired result?*). That's no different from the kind of
testing you did earlier in :doc:`Tutorial 1 `, using the
shell to examine the behavior of a method, or running the application and
entering data to check how it behaves.
@@ -326,6 +326,13 @@ in the shell::
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
+:meth:`~django.test.utils.setup_test_environment` installs a template renderer
+which will allow us to examine some additional attributes on responses such as
+``response.context`` that otherwise wouldn't be available. Note that this
+method *does not* setup a test database, so the following will be run against
+the existing database and the output may differ slightly depending on what
+polls you already created.
+
Next we need to import the test client class (later in ``tests.py`` we will use
the :class:`django.test.TestCase` class, which comes with its own client, so
this won't be required)::
diff --git a/docs/intro/tutorial06.txt b/docs/intro/tutorial06.txt
index 6b3d0f35e2..d5db3ae232 100644
--- a/docs/intro/tutorial06.txt
+++ b/docs/intro/tutorial06.txt
@@ -108,7 +108,7 @@ loaded in the bottom right of the screen.
These are the **basics**. For more details on settings and other bits included
with the framework see
-:doc:`the static files howto ` and the
+:doc:`the static files howto ` and
:doc:`the staticfiles reference `. :doc:`Deploying
static files ` discusses how to use static
files on a real server.
diff --git a/docs/ref/class-based-views/base.txt b/docs/ref/class-based-views/base.txt
index 2073458314..3ba7c38c43 100644
--- a/docs/ref/class-based-views/base.txt
+++ b/docs/ref/class-based-views/base.txt
@@ -105,6 +105,7 @@ TemplateView
in the URL.
.. versionchanged:: 1.5
+
The context used to be populated with a ``{{ params }}`` dictionary of
the parameters captured in the URL. Now those parameters are first-level
context variables.
diff --git a/docs/ref/class-based-views/generic-date-based.txt b/docs/ref/class-based-views/generic-date-based.txt
index 4144c382f8..4dcb788779 100644
--- a/docs/ref/class-based-views/generic-date-based.txt
+++ b/docs/ref/class-based-views/generic-date-based.txt
@@ -142,7 +142,7 @@ YearArchiveView
.. versionchanged:: 1.5
- Previously, this returned a string.
+ Previously, this returned a string.
* ``next_year``: A :class:`~datetime.date` object
representing the first day of the next year, according to
diff --git a/docs/ref/class-based-views/generic-editing.txt b/docs/ref/class-based-views/generic-editing.txt
index 1dbb427036..555ba40cfb 100644
--- a/docs/ref/class-based-views/generic-editing.txt
+++ b/docs/ref/class-based-views/generic-editing.txt
@@ -110,6 +110,7 @@ CreateView
class AuthorCreate(CreateView):
model = Author
+ fields = ['name']
UpdateView
----------
@@ -152,6 +153,7 @@ UpdateView
class AuthorUpdate(UpdateView):
model = Author
+ fields = ['name']
DeleteView
----------
diff --git a/docs/ref/class-based-views/mixins-date-based.txt b/docs/ref/class-based-views/mixins-date-based.txt
index 75f2a77615..1a1a4d531b 100644
--- a/docs/ref/class-based-views/mixins-date-based.txt
+++ b/docs/ref/class-based-views/mixins-date-based.txt
@@ -330,5 +330,6 @@ BaseDateListView
:meth:`QuerySet.dates()`.
.. versionchanged:: 1.5
+
The ``ordering`` parameter was added, and the default order was
changed to ascending.
diff --git a/docs/ref/class-based-views/mixins-editing.txt b/docs/ref/class-based-views/mixins-editing.txt
index a4175369aa..51d8628818 100644
--- a/docs/ref/class-based-views/mixins-editing.txt
+++ b/docs/ref/class-based-views/mixins-editing.txt
@@ -116,6 +116,18 @@ ModelFormMixin
by examining ``self.object`` or
:attr:`~django.views.generic.detail.SingleObjectMixin.queryset`.
+ .. attribute:: fields
+
+ .. versionadded:: 1.6
+
+ A list of names of fields. This is interpreted the same way as the
+ ``Meta.fields`` attribute of :class:`~django.forms.ModelForm`.
+
+ This is a required attribute if you are generating the form class
+ automatically (e.g. using ``model``). Omitting this attribute will
+ result in all fields being used, but this behaviour is deprecated
+ and will be removed in Django 1.8.
+
.. attribute:: success_url
The URL to redirect to when the form is successfully processed.
@@ -206,10 +218,10 @@ ProcessFormView
.. versionadded:: 1.6
- ``success_url`` may contain dictionary string formatting, which
- will be interpolated against the object's field attributes. For
- example, you could use ``success_url="/parent/%(parent_id)s/"`` to
- redirect to a URL composed out of the ``parent_id`` field on a model.
+ ``success_url`` may contain dictionary string formatting, which
+ will be interpolated against the object's field attributes. For
+ example, you could use ``success_url="/parent/%(parent_id)s/"`` to
+ redirect to a URL composed out of the ``parent_id`` field on a model.
.. method:: get_success_url()
diff --git a/docs/ref/class-based-views/mixins-simple.txt b/docs/ref/class-based-views/mixins-simple.txt
index 51b0386654..6796675529 100644
--- a/docs/ref/class-based-views/mixins-simple.txt
+++ b/docs/ref/class-based-views/mixins-simple.txt
@@ -67,7 +67,6 @@ TemplateResponseMixin
.. attribute:: content_type
.. versionadded:: 1.5
- The ``content_type`` attribute was added.
The content type to use for the response. ``content_type`` is passed
as a keyword argument to ``response_class``. Default is ``None`` --
diff --git a/docs/ref/clickjacking.txt b/docs/ref/clickjacking.txt
index ce27148ad3..0b81243b92 100644
--- a/docs/ref/clickjacking.txt
+++ b/docs/ref/clickjacking.txt
@@ -62,6 +62,7 @@ To set the same ``X-Frame-Options`` value for all responses in your site, put
)
.. versionchanged:: 1.6
+
This middleware is enabled in the settings file generated by
:djadmin:`startproject`.
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index c567bc1db4..67e498ee91 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -18,6 +18,7 @@ The admin is enabled in the default project template used by
:djadmin:`startproject`.
.. versionchanged:: 1.6
+
In previous versions, the admin wasn't enabled by default.
For reference, here are the requirements:
@@ -334,6 +335,22 @@ subclass::
For an example see the section `Adding custom validation to the admin`_.
+ .. admonition:: Note
+
+ .. versionchanged:: 1.6
+
+ If you define the ``Meta.model`` attribute on a
+ :class:`~django.forms.ModelForm`, you must also define the
+ ``Meta.fields`` attribute (or the ``Meta.exclude`` attribute). However,
+ since the admin has its own way of defining fields, the ``Meta.fields``
+ attribute will be ignored.
+
+ If the ``ModelForm`` is only going to be used for the admin, the easiest
+ solution is to omit the ``Meta.model`` attribute, since ``ModelAdmin``
+ will provide the correct model to use. Alternatively, you can set
+ ``fields = []`` in the ``Meta`` class to satisfy the validation on the
+ ``ModelForm``.
+
.. admonition:: Note
If your ``ModelForm`` and ``ModelAdmin`` both define an ``exclude``
@@ -1282,13 +1299,24 @@ templates used by the :class:`ModelAdmin` views:
on the changelist page. To use a custom form, for example::
class MyForm(forms.ModelForm):
- class Meta:
- model = MyModel
+ pass
class MyModelAdmin(admin.ModelAdmin):
def get_changelist_form(self, request, **kwargs):
return MyForm
+ .. admonition:: Note
+
+ .. versionchanged:: 1.6
+
+ If you define the ``Meta.model`` attribute on a
+ :class:`~django.forms.ModelForm`, you must also define the
+ ``Meta.fields`` attribute (or the ``Meta.exclude`` attribute). However,
+ ``ModelAdmin`` ignores this value, overriding it with the
+ :attr:`ModelAdmin.list_editable` attribute. The easiest solution is to
+ omit the ``Meta.model`` attribute, since ``ModelAdmin`` will provide the
+ correct model to use.
+
.. method:: ModelAdmin.get_changelist_formset(self, request, **kwargs)
Returns a :ref:`ModelFormSet ` class for use on the
@@ -1341,6 +1369,7 @@ templates used by the :class:`ModelAdmin` views:
return qs.filter(author=request.user)
.. versionchanged:: 1.6
+
The ``get_queryset`` method was previously named ``queryset``.
.. method:: ModelAdmin.message_user(request, message, level=messages.INFO, extra_tags='', fail_silently=False)
@@ -1348,7 +1377,9 @@ templates used by the :class:`ModelAdmin` views:
Sends a message to the user using the :mod:`django.contrib.messages`
backend. See the :ref:`custom ModelAdmin example `.
- .. versionadded:: 1.5
+ .. versionchanged:: 1.5
+
+ Keyword arguments were added in Django 1.5.
Keyword arguments allow you to change the message level, add extra CSS
tags, or fail silently if the ``contrib.messages`` framework is not
@@ -1451,6 +1482,7 @@ in your own admin JavaScript without including a second copy, you can use the
``django.jQuery`` object on changelist and add/edit views.
.. versionchanged:: 1.6
+
The embedded jQuery has been upgraded from 1.4.2 to 1.9.1.
The :class:`ModelAdmin` class requires jQuery by default, so there is no need
@@ -1485,9 +1517,6 @@ needed. Now within your form you can add your own custom validation for
any field::
class MyArticleAdminForm(forms.ModelForm):
- class Meta:
- model = Article
-
def clean_name(self):
# do something that validates your data
return self.cleaned_data["name"]
diff --git a/docs/ref/contrib/contenttypes.txt b/docs/ref/contrib/contenttypes.txt
index 388172c43e..4fa119bc70 100644
--- a/docs/ref/contrib/contenttypes.txt
+++ b/docs/ref/contrib/contenttypes.txt
@@ -234,18 +234,18 @@ lookup::
.. versionadded:: 1.5
-Prior to Django 1.5,
-:meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_model` and
-:meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_models`
-always returned the :class:`~django.contrib.contenttypes.models.ContentType`
-associated with the concrete model of the specified one(s). That means there
-was no way to retrieve the
-:class:`~django.contrib.contenttypes.models.ContentType` of a proxy model
-using those methods. As of Django 1.5 you can now pass a boolean flag â
-``for_concrete_model`` and ``for_concrete_models`` respectively â to specify
-wether or not you want to retrieve the
-:class:`~django.contrib.contenttypes.models.ContentType` for the concrete or
-direct model.
+ Prior to Django 1.5,
+ :meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_model` and
+ :meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_models`
+ always returned the :class:`~django.contrib.contenttypes.models.ContentType`
+ associated with the concrete model of the specified one(s). That means there
+ was no way to retrieve the
+ :class:`~django.contrib.contenttypes.models.ContentType` of a proxy model
+ using those methods. As of Django 1.5 you can now pass a boolean flag â
+ ``for_concrete_model`` and ``for_concrete_models`` respectively â to specify
+ wether or not you want to retrieve the
+ :class:`~django.contrib.contenttypes.models.ContentType` for the concrete or
+ direct model.
Generic relations
=================
diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt
index 97217e3c38..5e51e4cbf3 100644
--- a/docs/ref/contrib/gis/geoquerysets.txt
+++ b/docs/ref/contrib/gis/geoquerysets.txt
@@ -950,6 +950,7 @@ __ http://geohash.org/
*Availability*: PostGIS, SpatiaLite
.. versionchanged:: 1.5
+
``geojson`` support for Spatialite > 3.0 has been added.
Attaches a ``geojson`` attribute to every model in the queryset that contains the
diff --git a/docs/ref/contrib/gis/geos.txt b/docs/ref/contrib/gis/geos.txt
index 4d44638488..12e4e55165 100644
--- a/docs/ref/contrib/gis/geos.txt
+++ b/docs/ref/contrib/gis/geos.txt
@@ -845,7 +845,7 @@ include the SRID value (in other words, EWKB).
.. class:: WKBWriter
``WKBWriter`` provides the most control over its output. By default it
-returns OGC-compliant WKB when it's ``write`` method is called. However,
+returns OGC-compliant WKB when its ``write`` method is called. However,
it has properties that allow for the creation of EWKB, a superset of the
WKB standard that includes additional information.
diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt
index 74ebf6a35f..c9a1b405a3 100644
--- a/docs/ref/contrib/gis/install/geolibs.txt
+++ b/docs/ref/contrib/gis/install/geolibs.txt
@@ -194,7 +194,7 @@ Configure, make and install::
.. note::
- Because GeoDjango has it's own Python interface, the preceding instructions
+ Because GeoDjango has its own Python interface, the preceding instructions
do not build GDAL's own Python bindings. The bindings may be built by
adding the ``--with-python`` flag when running ``configure``. See
`GDAL/OGR In Python`__ for more information on GDAL's bindings.
diff --git a/docs/ref/contrib/sites.txt b/docs/ref/contrib/sites.txt
index 139a9b377f..65838dfa3e 100644
--- a/docs/ref/contrib/sites.txt
+++ b/docs/ref/contrib/sites.txt
@@ -252,6 +252,7 @@ Enabling the sites framework
============================
.. versionchanged:: 1.6
+
In previous versions, the sites framework was enabled by default.
To enable the sites framework, follow these steps:
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index 395abd90dd..7555acaaba 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -22,14 +22,14 @@ Persistent connections
.. versionadded:: 1.6
Persistent connections avoid the overhead of re-establishing a connection to
-the database in each request. By default, connections are kept open for up 10
-minutes â if not specified, :setting:`CONN_MAX_AGE` defaults to 600 seconds.
+the database in each request. They're controlled by the
+:setting:`CONN_MAX_AGE` parameter which defines the maximum lifetime of a
+connection. It can be set independently for each database.
-Django 1.5 and earlier didn't have persistent connections. To restore the
-legacy behavior of closing the connection at the end of every request, set
-:setting:`CONN_MAX_AGE` to ``0``.
-
-For unlimited persistent connections, set :setting:`CONN_MAX_AGE` to ``None``.
+The default value is ``0``, preserving the historical behavior of closing the
+database connection at the end of each request. To enable persistent
+connections, set :setting:`CONN_MAX_AGE` to a positive number of seconds. For
+unlimited persistent connections, set it to ``None``.
Connection management
~~~~~~~~~~~~~~~~~~~~~
@@ -64,13 +64,22 @@ least as many simultaneous connections as you have worker threads.
Sometimes a database won't be accessed by the majority of your views, for
example because it's the database of an external system, or thanks to caching.
-In such cases, you should set :setting:`CONN_MAX_AGE` to a lower value, or
-even ``0``, because it doesn't make sense to maintain a connection that's
-unlikely to be reused. This will help keep the number of simultaneous
-connections to this database small.
+In such cases, you should set :setting:`CONN_MAX_AGE` to a low value or even
+``0``, because it doesn't make sense to maintain a connection that's unlikely
+to be reused. This will help keep the number of simultaneous connections to
+this database small.
The development server creates a new thread for each request it handles,
-negating the effect of persistent connections.
+negating the effect of persistent connections. Don't enable them during
+development.
+
+When Django establishes a connection to the database, it sets up appropriate
+parameters, depending on the backend being used. If you enable persistent
+connections, this setup is no longer repeated every request. If you modify
+parameters such as the connection's isolation level or time zone, you should
+either restore Django's defaults at the end of each request, force an
+appropriate value at the beginning of each request, or disable persistent
+connections.
.. _postgresql-notes:
@@ -182,6 +191,7 @@ Django supports MySQL 5.0.3 and higher.
data on all database schema. Django's ``inspectdb`` feature uses it.
.. versionchanged:: 1.5
+
The minimum version requirement of MySQL 5.0.3 was set in Django 1.5.
Django expects the database to support Unicode (UTF-8 encoding) and delegates to
@@ -251,6 +261,18 @@ required for full MySQL support in Django.
.. _MySQLdb: http://sourceforge.net/projects/mysql-python
+Python 3
+--------
+
+At the time of writing, the latest release of MySQLdb (1.2.4) doesn't support
+Python 3. In order to use MySQL under Python 3, you'll have to install an
+unofficial fork, such as `MySQL-for-Python-3`_.
+
+This port is still in alpha. In particular, it doesn't support binary data,
+making it impossible to use :class:`django.db.models.BinaryField`.
+
+.. _MySQL-for-Python-3: https://github.com/clelland/MySQL-for-Python-3
+
Creating your database
----------------------
@@ -360,8 +382,8 @@ Here's a sample configuration which uses a MySQL option file::
default-character-set = utf8
Several other MySQLdb connection options may be useful, such as ``ssl``,
-``use_unicode``, ``init_command``, and ``sql_mode``. Consult the
-`MySQLdb documentation`_ for more details.
+``init_command``, and ``sql_mode``. Consult the `MySQLdb documentation`_ for
+more details.
.. _MySQL option file: http://dev.mysql.com/doc/refman/5.0/en/option-files.html
.. _MySQLdb documentation: http://mysql-python.sourceforge.net/
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index 1d3f1b8d1d..ec49705add 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -98,6 +98,7 @@ Can be run as a cronjob or directly to clean out old data from the database
(only expired sessions at the moment).
.. versionchanged:: 1.5
+
:djadmin:`cleanup` is deprecated. Use :djadmin:`clearsessions` instead.
compilemessages
@@ -122,7 +123,7 @@ Example usage::
.. versionchanged:: 1.6
-Added the ability to specify multiple locales.
+ Added the ability to specify multiple locales.
createcachetable
----------------
@@ -173,6 +174,7 @@ The :djadminopt:`--all` option may be provided to display all settings, even
if they have Django's default value. Such settings are prefixed by ``"###"``.
.. versionadded:: 1.6
+
The :djadminopt:`--all` option was added.
dumpdata
@@ -307,8 +309,8 @@ database to introspect.
.. versionchanged:: 1.6
-The behavior by which introspected models are created as unmanaged ones is new
-in Django 1.6.
+ The behavior by which introspected models are created as unmanaged ones is new
+ in Django 1.6.
loaddata
------------------------------
@@ -467,7 +469,7 @@ You can also use commas to separate multiple locales::
.. versionchanged:: 1.6
-Added the ability to specify multiple locales.
+ Added the ability to specify multiple locales.
.. django-admin-option:: --domain
@@ -778,8 +780,6 @@ use the ``--plain`` option, like so::
django-admin.py shell --plain
-.. versionchanged:: 1.5
-
If you would like to specify either IPython or bpython as your interpreter if
you have both installed you can specify an alternative interpreter interface
with the ``-i`` or ``--interface`` options like so:
@@ -807,9 +807,13 @@ behavior you can use the ``--no-startup`` option. e.g.::
django-admin.py shell --plain --no-startup
+.. versionadded:: 1.5
+
+ The ``--interface`` option was added in Django 1.5.
+
.. versionadded:: 1.6
-The ``--no-startup`` option was added in Django 1.6.
+ The ``--no-startup`` option was added in Django 1.6.
sql
-------------------------
@@ -1353,6 +1357,7 @@ for any other exception. If you specify ``--traceback``, ``django-admin.py``
will also output a full stack trace when a ``CommandError`` is raised.
.. versionchanged:: 1.6
+
Previously, Django didn't show a full stack trace by default for exceptions
other than ``CommandError``.
diff --git a/docs/ref/exceptions.txt b/docs/ref/exceptions.txt
index 93bb9ed251..f9a1715180 100644
--- a/docs/ref/exceptions.txt
+++ b/docs/ref/exceptions.txt
@@ -138,6 +138,7 @@ the underlying database exceptions. See :pep:`249`, the Python Database API
Specification v2.0, for further information.
.. versionchanged:: 1.6
+
Previous version of Django only wrapped ``DatabaseError`` and
``IntegrityError``.
diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
index c8b8044d26..29f889445d 100644
--- a/docs/ref/forms/fields.txt
+++ b/docs/ref/forms/fields.txt
@@ -467,6 +467,11 @@ For each field, we describe the default widget used if you don't specify
The ``max_value`` and ``min_value`` error messages may contain
``%(limit_value)s``, which will be substituted by the appropriate limit.
+ .. versionchanged:: 1.6
+
+ Similarly, the ``max_digits``, ``max_decimal_places`` and
+ ``max_whole_digits`` error messages may contain ``%(max)s``.
+
Takes four optional arguments:
.. attribute:: max_value
@@ -1010,9 +1015,16 @@ objects (in the case of ``ModelMultipleChoiceField``) into the
``invalid_pk_value``
.. versionchanged:: 1.5
+
The empty and normalized values were changed to be consistently
``QuerySets`` instead of ``[]`` and ``QuerySet`` respectively.
+ .. versionchanged:: 1.6
+
+ The ``invalid_choice`` message may contain ``%(value)s`` and the
+ ``invalid_pk_value`` message may contain ``%(pk)s``, which will be
+ substituted by the appropriate values.
+
Allows the selection of one or more model objects, suitable for
representing a many-to-many relation. As with :class:`ModelChoiceField`,
you can use ``label_from_instance`` to customize the object
diff --git a/docs/ref/forms/models.txt b/docs/ref/forms/models.txt
index dd0a422fd0..9b3480758a 100644
--- a/docs/ref/forms/models.txt
+++ b/docs/ref/forms/models.txt
@@ -25,6 +25,14 @@ Model Form Functions
See :ref:`modelforms-factory` for example usage.
+ .. versionchanged:: 1.6
+
+ You must provide the list of fields explicitly, either via keyword arguments
+ ``fields`` or ``exclude``, or the corresponding attributes on the form's
+ inner ``Meta`` class. See :ref:`modelforms-selecting-fields` for more
+ information. Omitting any definition of the fields to use will result in all
+ fields being used, but this behaviour is deprecated.
+
.. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False)
Returns a ``FormSet`` class for the given ``model`` class.
@@ -42,7 +50,7 @@ Model Form Functions
.. versionchanged:: 1.6
- The ``widgets`` and the ``validate_max`` parameters were added.
+ The ``widgets`` and the ``validate_max`` parameters were added.
.. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False)
@@ -57,4 +65,4 @@ Model Form Functions
.. versionchanged:: 1.6
- The ``widgets`` and the ``validate_max`` parameters were added.
+ The ``widgets`` and the ``validate_max`` parameters were added.
diff --git a/docs/ref/forms/validation.txt b/docs/ref/forms/validation.txt
index 978c985b55..3aaa69b6ea 100644
--- a/docs/ref/forms/validation.txt
+++ b/docs/ref/forms/validation.txt
@@ -359,7 +359,7 @@ considering aren't valid, we must remember to remove them from the
.. versionchanged:: 1.5
-Django used to remove the ``cleaned_data`` attribute entirely if there were
-any errors in the form. Since version 1.5, ``cleaned_data`` is present even if
-the form doesn't validate, but it contains only field values that did
-validate.
+ Django used to remove the ``cleaned_data`` attribute entirely if there were
+ any errors in the form. Since version 1.5, ``cleaned_data`` is present even if
+ the form doesn't validate, but it contains only field values that did
+ validate.
diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
index 514e8b3dc0..678f2e6949 100644
--- a/docs/ref/forms/widgets.txt
+++ b/docs/ref/forms/widgets.txt
@@ -522,6 +522,7 @@ Selector and checkbox widgets
``True`` if the checkbox should be checked for that value.
.. versionchanged:: 1.5
+
Exceptions from ``check_test`` used to be silenced by its caller,
this is no longer the case, they will propagate upwards.
diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt
index 20bb2fb751..03885a2215 100644
--- a/docs/ref/middleware.txt
+++ b/docs/ref/middleware.txt
@@ -206,6 +206,7 @@ Transaction middleware
.. class:: TransactionMiddleware
.. versionchanged:: 1.6
+
``TransactionMiddleware`` is deprecated. The documentation of transactions
contains :ref:`upgrade instructions `.
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index 7ef251c907..d322904ec9 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -378,6 +378,7 @@ If you need to accept :attr:`~Field.null` values then use
:class:`NullBooleanField` instead.
.. versionchanged:: 1.6
+
The default value of ``BooleanField`` was changed from ``False`` to
``None`` when :attr:`Field.default` isn't defined.
@@ -956,8 +957,8 @@ Like all :class:`CharField` subclasses, :class:`URLField` takes the optional
.. versionadded:: 1.5
-The current value of the field will be displayed as a clickable link above the
-input widget.
+ The current value of the field will be displayed as a clickable link above the
+ input widget.
Relationship fields
diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt
index 9f583c42ac..b4b162a9ea 100644
--- a/docs/ref/models/instances.txt
+++ b/docs/ref/models/instances.txt
@@ -297,8 +297,9 @@ follows this algorithm:
didn't update anything, Django executes an ``INSERT``.
.. versionchanged:: 1.6
- Previously Django used ``SELECT`` - if not found ``INSERT`` else ``UPDATE``
- algorithm. The old algorithm resulted in one more query in ``UPDATE`` case.
+
+ Previously Django used ``SELECT`` - if not found ``INSERT`` else ``UPDATE``
+ algorithm. The old algorithm resulted in one more query in ``UPDATE`` case.
The one gotcha here is that you should be careful not to specify a primary-key
value explicitly when saving new objects, if you cannot guarantee the
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 9c1337d59f..d27214a66c 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -554,11 +554,13 @@ Returns a ``DateQuerySet`` â a ``QuerySet`` that evaluates to a list of
particular kind within the contents of the ``QuerySet``.
.. versionchanged:: 1.6
+
``dates`` used to return a list of :class:`datetime.datetime` objects.
``field`` should be the name of a ``DateField`` of your model.
.. versionchanged:: 1.6
+
``dates`` used to accept operating on a ``DateTimeField``.
``kind`` should be either ``"year"``, ``"month"`` or ``"day"``. Each
@@ -625,7 +627,8 @@ object. If it's ``None``, Django uses the :ref:`current time zone
- SQLite: install pytz_ â conversions are actually performed in Python.
- PostgreSQL: no requirements (see `Time Zones`_).
- Oracle: no requirements (see `Choosing a Time Zone File`_).
- - MySQL: load the time zone tables with `mysql_tzinfo_to_sql`_.
+ - MySQL: install pytz_ and load the time zone tables with
+ `mysql_tzinfo_to_sql`_.
.. _pytz: http://pytz.sourceforge.net/
.. _Time Zones: http://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-TIMEZONES
@@ -1121,11 +1124,11 @@ to ``defer()``::
.. versionchanged:: 1.5
-Some fields in a model won't be deferred, even if you ask for them. You can
-never defer the loading of the primary key. If you are using
-:meth:`select_related()` to retrieve related models, you shouldn't defer the
-loading of the field that connects from the primary model to the related
-one, doing so will result in an error.
+ Some fields in a model won't be deferred, even if you ask for them. You can
+ never defer the loading of the primary key. If you are using
+ :meth:`select_related()` to retrieve related models, you shouldn't defer the
+ loading of the field that connects from the primary model to the related
+ one, doing so will result in an error.
.. note::
@@ -1193,20 +1196,20 @@ logically::
# existing set of fields).
Entry.objects.defer("body").only("headline", "body")
-.. versionchanged:: 1.5
-
All of the cautions in the note for the :meth:`defer` documentation apply to
``only()`` as well. Use it cautiously and only after exhausting your other
-options. Also note that using :meth:`only` and omitting a field requested
-using :meth:`select_related` is an error as well.
+options.
.. versionchanged:: 1.5
-.. note::
+ Using :meth:`only` and omitting a field requested using
+ :meth:`select_related` is an error as well.
- When calling :meth:`~django.db.models.Model.save()` for instances with
- deferred fields, only the loaded fields will be saved. See
- :meth:`~django.db.models.Model.save()` for more details.
+ .. note::
+
+ When calling :meth:`~django.db.models.Model.save()` for instances with
+ deferred fields, only the loaded fields will be saved. See
+ :meth:`~django.db.models.Model.save()` for more details.
using
~~~~~
@@ -1424,6 +1427,7 @@ query. The default is to create all objects in one batch, except for SQLite
where the default is such that at maximum 999 variables per query is used.
.. versionadded:: 1.5
+
The ``batch_size`` parameter was added in version 1.5.
count
@@ -1725,7 +1729,8 @@ methods on your models. It does, however, emit the
(including cascaded deletions).
.. versionadded:: 1.5
- Allow fast-path deletion of objects
+
+ Allow fast-path deletion of objects.
Django needs to fetch objects into memory to send signals and handle cascades.
However, if there are no cascades and no signals, then Django may take a
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index 0f62741c5d..2fac7f2f9c 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -94,6 +94,7 @@ All attributes should be considered read-only, unless stated otherwise below.
:attr:`HttpRequest.body` attribute instead.
.. versionchanged:: 1.5
+
Before Django 1.5, HttpRequest.POST contained non-form data.
It's possible that a request can come in via POST with an empty ``POST``
@@ -563,19 +564,19 @@ streaming response if (and only if) no middleware accesses the
.. versionchanged:: 1.5
-This technique is fragile and was deprecated in Django 1.5. If you need the
-response to be streamed from the iterator to the client, you should use the
-:class:`StreamingHttpResponse` class instead.
+ This technique is fragile and was deprecated in Django 1.5. If you need the
+ response to be streamed from the iterator to the client, you should use the
+ :class:`StreamingHttpResponse` class instead.
-As of Django 1.7, when :class:`HttpResponse` is instantiated with an
-iterator, it will consume it immediately, store the response content as a
-string, and discard the iterator.
+ As of Django 1.7, when :class:`HttpResponse` is instantiated with an
+ iterator, it will consume it immediately, store the response content as a
+ string, and discard the iterator.
.. versionchanged:: 1.5
-You can now use :class:`HttpResponse` as a file-like object even if it was
-instantiated with an iterator. Django will consume and save the content of
-the iterator on first access.
+ You can now use :class:`HttpResponse` as a file-like object even if it was
+ instantiated with an iterator. Django will consume and save the content of
+ the iterator on first access.
Setting headers
~~~~~~~~~~~~~~~
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index 1fc9d2ff92..04b42aeeb2 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -67,7 +67,7 @@ A list of strings representing the host/domain names that this Django site can
serve. This is a security measure to prevent an attacker from poisoning caches
and password reset emails with links to malicious hosts by submitting requests
with a fake HTTP ``Host`` header, which is possible even under many
-seemingly-safe webserver configurations.
+seemingly-safe web server configurations.
Values in this list can be fully qualified names (e.g. ``'www.example.com'``),
in which case they will be matched against the request's ``Host`` header
@@ -79,6 +79,20 @@ responsible to provide your own validation of the ``Host`` header (perhaps in a
middleware; if so this middleware must be listed first in
:setting:`MIDDLEWARE_CLASSES`).
+.. note::
+
+ If you want to also allow the `fully qualified domain name (FQDN)`_, which
+ some browsers can send in the Host header, you must explicitly add another
+ ALLOWED_HOSTS entry that includes a trailing period. This entry can also be
+ a subdomain wildcard::
+
+ ALLOWED_HOSTS = [
+ '.example.com', # Allow domain and subdomains
+ '.example.com.', # Also allow FQDN and subdomains
+ ]
+
+.. _`fully qualified domain name (FQDN)`: http://en.wikipedia.org/wiki/Fully_qualified_domain_name
+
If the ``Host`` header (or ``X-Forwarded-Host`` if
:setting:`USE_X_FORWARDED_HOST` is enabled) does not match any value in this
list, the :meth:`django.http.HttpRequest.get_host()` method will raise
@@ -495,7 +509,7 @@ CONN_MAX_AGE
.. versionadded:: 1.6
-Default: ``600``
+Default: ``0``
The lifetime of a database connection, in seconds. Use ``0`` to close database
connections at the end of each request â Django's historical behavior â and
@@ -1265,9 +1279,9 @@ see the current list of translated languages by looking in
.. _online source: https://github.com/django/django/blob/master/django/conf/global_settings.py
-The list is a tuple of two-tuples in the format
-(:term:`language code`, ``language name``) -- for example,
-``('ja', 'Japanese')``.
+The list is a tuple of two-tuples in the format
+(:term:`language code`, ``language name``) -- for example,
+``('ja', 'Japanese')``.
This specifies which languages are available for language selection. See
:doc:`/topics/i18n/index`.
@@ -1486,6 +1500,7 @@ randomly-generated ``SECRET_KEY`` to each new project.
execution vulnerabilities.
.. versionchanged:: 1.5
+
Django will now refuse to start if :setting:`SECRET_KEY` is not set.
.. setting:: SECURE_PROXY_SSL_HEADER
@@ -1771,7 +1786,7 @@ See also :setting:`DATE_INPUT_FORMATS` and :setting:`DATETIME_INPUT_FORMATS`.
.. versionchanged:: 1.6
-Input format with microseconds has been added.
+ Input format with microseconds has been added.
.. _datetime: http://docs.python.org/library/datetime.html#strftime-strptime-behavior
@@ -2055,11 +2070,11 @@ decorator, for example.
.. versionchanged:: 1.5
-This setting now also accepts view function names and
-:ref:`named URL patterns ` which can be used to reduce
-configuration duplication since you no longer have to define the URL in two
-places (``settings`` and URLconf).
-For backward compatibility reasons the default remains unchanged.
+ This setting now also accepts view function names and
+ :ref:`named URL patterns ` which can be used to reduce
+ configuration duplication since you no longer have to define the URL in two
+ places (``settings`` and URLconf).
+ For backward compatibility reasons the default remains unchanged.
.. setting:: LOGIN_URL
@@ -2073,11 +2088,11 @@ The URL where requests are redirected for login, especially when using the
.. versionchanged:: 1.5
-This setting now also accepts view function names and
-:ref:`named URL patterns ` which can be used to reduce
-configuration duplication since you no longer have to define the URL in two
-places (``settings`` and URLconf).
-For backward compatibility reasons the default remains unchanged.
+ This setting now also accepts view function names and
+ :ref:`named URL patterns ` which can be used to reduce
+ configuration duplication since you no longer have to define the URL in two
+ places (``settings`` and URLconf).
+ For backward compatibility reasons the default remains unchanged.
.. setting:: LOGOUT_URL
diff --git a/docs/ref/template-response.txt b/docs/ref/template-response.txt
index 5c13ec7d96..cdefe2fae8 100644
--- a/docs/ref/template-response.txt
+++ b/docs/ref/template-response.txt
@@ -78,13 +78,13 @@ Methods
.. versionchanged:: 1.5
- Historically, this parameter was only called ``mimetype`` (now
- deprecated), but since this is actually the value included in the HTTP
- ``Content-Type`` header, it can also include the character set
- encoding, which makes it more than just a MIME type specification. If
- ``mimetype`` is specified (not ``None``), that value is used.
- Otherwise, ``content_type`` is used. If neither is given,
- :setting:`DEFAULT_CONTENT_TYPE` is used.
+ Historically, this parameter was only called ``mimetype`` (now
+ deprecated), but since this is actually the value included in the HTTP
+ ``Content-Type`` header, it can also include the character set
+ encoding, which makes it more than just a MIME type specification. If
+ ``mimetype`` is specified (not ``None``), that value is used.
+ Otherwise, ``content_type`` is used. If neither is given,
+ :setting:`DEFAULT_CONTENT_TYPE` is used.
.. method:: SimpleTemplateResponse.resolve_context(context)
diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt
index 0162f78eed..677aa13cbb 100644
--- a/docs/ref/templates/api.txt
+++ b/docs/ref/templates/api.txt
@@ -272,6 +272,7 @@ Every context contains ``True``, ``False`` and ``None``. As you would expect,
these variables resolve to the corresponding Python objects.
.. versionadded:: 1.5
+
Before Django 1.5, these variables weren't a special case, and they
resolved to ``None`` unless you defined them in the context.
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 123e114c4a..287fd4f59e 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -176,7 +176,7 @@ just declare the cycle, but not output the first value, you can add a
This will output a list of ``
`` elements with ``class``
alternating between ``row1`` and ``row2``; the subtemplate will have
-access to ``rowcolors`` in it's context that matches the class of the
+access to ``rowcolors`` in its context that matches the class of the
``
`` that encloses it. If the ``silent`` keyword were to be
omitted, ``row1`` would be emitted as normal text, outside the
``
`` element.
@@ -191,19 +191,19 @@ call to ``{% cycle %}`` doesn't specify silent::
.. versionchanged:: 1.6
-To improve safety, future versions of ``cycle`` will automatically escape
-their output. You're encouraged to activate this behavior by loading
-``cycle`` from the ``future`` template library::
+ To improve safety, future versions of ``cycle`` will automatically escape
+ their output. You're encouraged to activate this behavior by loading
+ ``cycle`` from the ``future`` template library::
- {% load cycle from future %}
+ {% load cycle from future %}
-When using the ``future`` version, you can disable auto-escaping with::
+ When using the ``future`` version, you can disable auto-escaping with::
- {% for o in some_list %}
-
- ...
-
- {% endfor %}
+ {% for o in some_list %}
+
+ ...
+
+ {% endfor %}
.. templatetag:: debug
@@ -294,21 +294,21 @@ to escape the variables in the firstof tag, you must do so explicitly::
.. versionchanged:: 1.6
-To improve safety, future versions of ``firstof`` will automatically escape
-their output. You're encouraged to activate this behavior by loading
-``firstof`` from the ``future`` template library::
+ To improve safety, future versions of ``firstof`` will automatically escape
+ their output. You're encouraged to activate this behavior by loading
+ ``firstof`` from the ``future`` template library::
- {% load firstof from future %}
+ {% load firstof from future %}
-When using the ``future`` version, you can disable auto-escaping with::
+ When using the ``future`` version, you can disable auto-escaping with::
- {% autoescape off %}
- {% firstof var1 var2 var3 "fallback value" %}
- {% endautoescape %}
+ {% autoescape off %}
+ {% firstof var1 var2 var3 "fallback value" %}
+ {% endautoescape %}
-Or if only some variables should be escaped, you can use::
+ Or if only some variables should be escaped, you can use::
- {% firstof var1 var2|safe var3 "fallback value"|safe %}
+ {% firstof var1 var2|safe var3 "fallback value"|safe %}
.. templatetag:: for
@@ -657,11 +657,6 @@ The arguments can be hard-coded strings, so the following is valid::
...
{% endifequal %}
-It is only possible to compare an argument to template variables or strings.
-You cannot check for equality with Python objects such as ``True`` or
-``False``. If you need to test if something is true or false, use the
-:ttag:`if` tag instead.
-
An alternative to the ``ifequal`` tag is to use the :ttag:`if` tag and the
``==`` operator.
@@ -1065,6 +1060,7 @@ by the context as to the current application.
Don't forget to put quotes around the function path or pattern name!
.. versionchanged:: 1.5
+
The first parameter used not to be quoted, which was inconsistent with
other template tags. Since Django 1.5, it is evaluated according to
the usual rules: it can be a quoted string or a variable that will be
diff --git a/docs/ref/unicode.txt b/docs/ref/unicode.txt
index bd5bdc96a9..e5074285e4 100644
--- a/docs/ref/unicode.txt
+++ b/docs/ref/unicode.txt
@@ -47,26 +47,26 @@ You can use Unicode strings, or you can use normal strings (sometimes called
.. versionchanged:: 1.5
-In Python 3, the logic is reversed, that is normal strings are Unicode, and
-when you want to specifically create a bytestring, you have to prefix the
-string with a 'b'. As we are doing in Django code from version 1.5,
-we recommend that you import ``unicode_literals`` from the __future__ library
-in your code. Then, when you specifically want to create a bytestring literal,
-prefix the string with 'b'.
+ In Python 3, the logic is reversed, that is normal strings are Unicode, and
+ when you want to specifically create a bytestring, you have to prefix the
+ string with a 'b'. As we are doing in Django code from version 1.5,
+ we recommend that you import ``unicode_literals`` from the __future__ library
+ in your code. Then, when you specifically want to create a bytestring literal,
+ prefix the string with 'b'.
-Python 2 legacy::
+ Python 2 legacy::
- my_string = "This is a bytestring"
- my_unicode = u"This is an Unicode string"
+ my_string = "This is a bytestring"
+ my_unicode = u"This is an Unicode string"
-Python 2 with unicode literals or Python 3::
+ Python 2 with unicode literals or Python 3::
- from __future__ import unicode_literals
+ from __future__ import unicode_literals
- my_string = b"This is a bytestring"
- my_unicode = "This is an Unicode string"
+ my_string = b"This is a bytestring"
+ my_unicode = "This is an Unicode string"
-See also :doc:`Python 3 compatibility `.
+ See also :doc:`Python 3 compatibility `.
.. warning::
diff --git a/docs/ref/views.txt b/docs/ref/views.txt
index 3753f83f07..8c9c3e3ed8 100644
--- a/docs/ref/views.txt
+++ b/docs/ref/views.txt
@@ -18,7 +18,7 @@ convenience, you'd like to have Django serve for you in local development.
The :func:`~django.views.static.serve` view can be used to serve any directory
you give it. (This view is **not** hardened for production use and should be
used only as a development aid; you should serve these files in production
-using a real front-end webserver).
+using a real front-end web server).
The most likely example is user-uploaded content in :setting:`MEDIA_ROOT`.
``django.contrib.staticfiles`` is intended for static assets and has no
diff --git a/docs/releases/1.0.txt b/docs/releases/1.0.txt
index be61311232..65ecde2fea 100644
--- a/docs/releases/1.0.txt
+++ b/docs/releases/1.0.txt
@@ -66,6 +66,7 @@ We can't possibly document everything that's new in 1.0, but the documentation
will be your definitive guide. Anywhere you see something like:
.. versionadded:: 1.0
+
This feature is new in Django 1.0
You'll know that you're looking at something new or changed.
diff --git a/docs/releases/1.3-alpha-1.txt b/docs/releases/1.3-alpha-1.txt
index c71736dc60..42947d9a44 100644
--- a/docs/releases/1.3-alpha-1.txt
+++ b/docs/releases/1.3-alpha-1.txt
@@ -87,7 +87,7 @@ To access this library, Django provides the
``django.utils.unittest`` module alias. If you are using Python
2.7, or you have installed unittest2 locally, Django will map the
alias to the installed version of the unittest library. Otherwise,
-Django will use it's own bundled version of unittest2.
+Django will use its own bundled version of unittest2.
To use this alias, simply use::
diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt
index 2f06756a1b..9cce36aac3 100644
--- a/docs/releases/1.6.txt
+++ b/docs/releases/1.6.txt
@@ -66,13 +66,8 @@ Persistent database connections
Django now supports reusing the same database connection for several requests.
This avoids the overhead of re-establishing a connection at the beginning of
-each request.
-
-By default, database connections will kept open for 10 minutes. This behavior
-is controlled by the :setting:`CONN_MAX_AGE` setting. To restore the previous
-behavior of closing the connection at the end of each request, set
-:setting:`CONN_MAX_AGE` to ``0``. See :ref:`persistent-database-connections`
-for details.
+each request. For backwards compatibility, this feature is disabled by
+default. See :ref:`persistent-database-connections` for details.
Time zone aware aggregation
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -298,21 +293,6 @@ Django 1.6 introduces ``hour``, ``minute``, and ``second`` lookups on
``hour``, ``minute``, or ``second``, the new lookups will clash with you field
names. Append an explicit :lookup:`exact` lookup if this is an issue.
-Persistent database connections
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Connection setup not repeated for each request
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-When Django establishes a connection to the database, it sets up appropriate
-parameters, depending on the backend being used. Since `persistent database
-connections `_ are enabled by default in
-Django 1.6, this setup isn't repeated at every request any more. If you
-modifiy parameters such as the connection's isolation level or time zone, you
-should either restore Django's defaults at the end of each request, force an
-appropriate value at the beginning of each request, or disable persistent
-connections.
-
``BooleanField`` no longer defaults to ``False``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -423,6 +403,13 @@ Miscellaneous
``type='email'``, ``type='url'`` or ``type='number'`` depending on their
corresponding field type.
+* Form field's :attr:`~django.forms.Field.error_messages` that contain a
+ placeholder should now always use a named placeholder (``"Value '%(value)s' is
+ too big"`` instead of ``"Value '%s' is too big"``). See the corresponding
+ field documentation for details about the names of the placeholders. The
+ changes in 1.6 particularly affect :class:`~django.forms.DecimalField` and
+ :class:`~django.forms.ModelMultipleChoiceField`.
+
Features deprecated in 1.6
==========================
@@ -545,3 +532,55 @@ including it in an URLconf, simply replace::
with::
(r'^prefix/(?P\d+)/(?P.*)/$', 'django.contrib.contenttypes.views.shortcut'),
+
+``ModelForm`` without ``fields`` or ``exclude``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Previously, if you wanted a :class:`~django.forms.ModelForm` to use all fields on
+the model, you could simply omit the ``Meta.fields`` attribute, and all fields
+would be used.
+
+This can lead to security problems where fields are added to the model and,
+unintentionally, automatically become editable by end users. In some cases,
+particular with boolean fields, it is possible for this problem to be completely
+invisible. This is a form of `Mass assignment vulnerability
+`_.
+
+For this reason, this behaviour is deprecated, and using the ``Meta.exclude``
+option is strongly discouraged. Instead, all fields that are intended for
+inclusion in the form should be listed explicitly in the ``fields`` attribute.
+
+If this security concern really does not apply in your case, there is a shortcut
+to explicitly indicate that all fields should be used - use the special value
+``"__all__"`` for the fields attribute::
+
+ class MyModelForm(ModelForm):
+ class Meta:
+ fields = "__all__"
+ model = MyModel
+
+If you have custom ``ModelForms`` that only need to be used in the admin, there
+is another option. The admin has its own methods for defining fields
+(``fieldsets`` etc.), and so adding a list of fields to the ``ModelForm`` is
+redundant. Instead, simply omit the ``Meta`` inner class of the ``ModelForm``,
+or omit the ``Meta.model`` attribute. Since the ``ModelAdmin`` subclass knows
+which model it is for, it can add the necessary attributes to derive a
+functioning ``ModelForm``. This behaviour also works for earlier Django
+versions.
+
+``UpdateView`` and ``CreateView`` without explicit fields
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The generic views :class:`~django.views.generic.edit.CreateView` and
+:class:`~django.views.generic.edit.UpdateView`, and anything else derived from
+:class:`~django.views.generic.edit.ModelFormMixin`, are vulnerable to the
+security problem described in the section above, because they can automatically
+create a ``ModelForm`` that uses all fields for a model.
+
+For this reason, if you use these views for editing models, you must also supply
+the ``fields`` attribute, which is a list of model fields and works in the same
+way as the :class:`~django.forms.ModelForm` ``Meta.fields`` attribute. Alternatively,
+you can set set the ``form_class`` attribute to a ``ModelForm`` that explicitly
+defines the fields to be used. Defining an ``UpdateView`` or ``CreateView``
+subclass to be used with a model but without an explicit list of fields is
+deprecated.
diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt
index 143a729f37..b53bbe8211 100644
--- a/docs/topics/auth/customizing.txt
+++ b/docs/topics/auth/customizing.txt
@@ -83,9 +83,9 @@ processing at the first positive match.
.. versionadded:: 1.6
-If a backend raises a :class:`~django.core.exceptions.PermissionDenied`
-exception, authentication will immediately fail. Django won't check the
-backends that follow.
+ If a backend raises a :class:`~django.core.exceptions.PermissionDenied`
+ exception, authentication will immediately fail. Django won't check the
+ backends that follow.
Writing an authentication backend
---------------------------------
@@ -1051,6 +1051,7 @@ code would be required in the app's ``admin.py`` file::
class Meta:
model = MyUser
+ fields = ['email', 'password', 'date_of_birth', 'is_active', 'is_admin']
def clean_password(self):
# Regardless of what the user provides, return the initial value.
diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt
index a38ee84841..e666cded75 100644
--- a/docs/topics/auth/default.txt
+++ b/docs/topics/auth/default.txt
@@ -435,10 +435,10 @@ The login_required decorator
.. versionchanged:: 1.5
- The :setting:`settings.LOGIN_URL ` also accepts
- view function names and :ref:`named URL patterns `.
- This allows you to freely remap your login view within your URLconf
- without having to update the setting.
+ The :setting:`settings.LOGIN_URL ` also accepts
+ view function names and :ref:`named URL patterns `.
+ This allows you to freely remap your login view within your URLconf
+ without having to update the setting.
.. note::
@@ -759,6 +759,7 @@ patterns.
mail will be sent either.
.. versionchanged:: 1.6
+
Previously, error messages indicated whether a given email was
registered.
@@ -1041,6 +1042,7 @@ Thus, you can check permissions in template ``{% if %}`` statements:
{% endif %}
.. versionadded:: 1.5
+
Permission lookup by "if in".
It is possible to also look permissions up by ``{% if in %}`` statements.
diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt
index 8cd34f8ad9..86c5280159 100644
--- a/docs/topics/class-based-views/generic-editing.txt
+++ b/docs/topics/class-based-views/generic-editing.txt
@@ -114,9 +114,11 @@ here; we don't have to write any logic ourselves::
class AuthorCreate(CreateView):
model = Author
+ fields = ['name']
class AuthorUpdate(UpdateView):
model = Author
+ fields = ['name']
class AuthorDelete(DeleteView):
model = Author
@@ -126,6 +128,17 @@ here; we don't have to write any logic ourselves::
We have to use :func:`~django.core.urlresolvers.reverse_lazy` here, not
just ``reverse`` as the urls are not loaded when the file is imported.
+.. versionchanged:: 1.6
+
+In Django 1.6, the ``fields`` attribute was added, which works the same way as
+the ``fields`` attribute on the inner ``Meta`` class on
+:class:`~django.forms.ModelForm`.
+
+Omitting the fields attribute will work as previously, but is deprecated and
+this attribute will be required from 1.8 (unless you define the form class in
+another way).
+
+
Finally, we hook these new views into the URLconf::
# urls.py
@@ -177,33 +190,17 @@ the foreign key relation to the model::
# ...
-Create a custom :class:`~django.forms.ModelForm` in order to exclude the
-``created_by`` field and prevent the user from editing it:
-
-.. code-block:: python
-
- # forms.py
- from django import forms
- from myapp.models import Author
-
- class AuthorForm(forms.ModelForm):
- class Meta:
- model = Author
- exclude = ('created_by',)
-
-In the view, use the custom
-:attr:`~django.views.generic.edit.FormMixin.form_class` and override
-:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the
-user::
+In the view, ensure that you exclude ``created_by`` in the list of fields to
+edit, and override
+:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the user::
# views.py
from django.views.generic.edit import CreateView
from myapp.models import Author
- from myapp.forms import AuthorForm
class AuthorCreate(CreateView):
- form_class = AuthorForm
model = Author
+ fields = ['name']
def form_valid(self, form):
form.instance.created_by = self.request.user
@@ -237,19 +234,24 @@ works for AJAX requests as well as 'normal' form POSTs::
return HttpResponse(data, **response_kwargs)
def form_invalid(self, form):
+ response = super(AjaxableResponseMixin, self).form_invalid(form)
if self.request.is_ajax():
return self.render_to_json_response(form.errors, status=400)
else:
- return super(AjaxableResponseMixin, self).form_invalid(form)
+ return response
def form_valid(self, form):
+ # We make sure to call the parent's form_valid() method because
+ # it might do some processing (in the case of CreateView, it will
+ # call form.save() for example).
+ response = super(AjaxableResponseMixin, self).form_valid(form)
if self.request.is_ajax():
data = {
- 'pk': form.instance.pk,
+ 'pk': self.object.pk,
}
return self.render_to_json_response(data)
else:
- return super(AjaxableResponseMixin, self).form_valid(form)
+ return response
class AuthorCreate(AjaxableResponseMixin, CreateView):
model = Author
diff --git a/docs/topics/db/managers.txt b/docs/topics/db/managers.txt
index 56bdd16e84..2a0f7e4ce0 100644
--- a/docs/topics/db/managers.txt
+++ b/docs/topics/db/managers.txt
@@ -176,6 +176,7 @@ your choice of default manager in order to avoid a situation where overriding
work with.
.. versionchanged:: 1.6
+
The ``get_queryset`` method was previously named ``get_query_set``.
.. _managers-for-related-objects:
diff --git a/docs/topics/db/multi-db.txt b/docs/topics/db/multi-db.txt
index 182099cc3a..ac329cc4fc 100644
--- a/docs/topics/db/multi-db.txt
+++ b/docs/topics/db/multi-db.txt
@@ -681,6 +681,7 @@ In addition, some objects are automatically created just after
database).
.. versionchanged:: 1.5
+
Previously, ``ContentType`` and ``Permission`` instances were created only
in the default database.
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index f19302974d..2553eac27a 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -638,6 +638,7 @@ that were modified more than 3 days after they were published::
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
.. versionadded:: 1.5
+
``.bitand()`` and ``.bitor()``
The ``F()`` objects now support bitwise operations by ``.bitand()`` and
@@ -646,6 +647,7 @@ The ``F()`` objects now support bitwise operations by ``.bitand()`` and
>>> F('somefield').bitand(16)
.. versionchanged:: 1.5
+
The previously undocumented operators ``&`` and ``|`` no longer produce
bitwise operations, use ``.bitand()`` and ``.bitor()`` instead.
@@ -1110,15 +1112,6 @@ above example code would look like this::
>>> b.entries.filter(headline__contains='Lennon')
>>> b.entries.count()
-You cannot access a reverse :class:`~django.db.models.ForeignKey`
-:class:`~django.db.models.Manager` from the class; it must be accessed from an
-instance::
-
- >>> Blog.entry_set
- Traceback:
- ...
- AttributeError: "Manager must be accessed via instance".
-
In addition to the :class:`~django.db.models.query.QuerySet` methods defined in
"Retrieving objects" above, the :class:`~django.db.models.ForeignKey`
:class:`~django.db.models.Manager` has additional methods used to handle the
diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt
index 34cfa382d3..2ec31a4988 100644
--- a/docs/topics/db/sql.txt
+++ b/docs/topics/db/sql.txt
@@ -211,7 +211,7 @@ For example::
from django.db import connection
- def my_custom_sql():
+ def my_custom_sql(self):
cursor = connection.cursor()
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
@@ -222,6 +222,7 @@ For example::
return row
.. versionchanged:: 1.6
+
In Django 1.5 and earlier, after performing a data changing operation, you
had to call ``transaction.commit_unless_managed()`` to ensure your changes
were committed to the database. Since Django now defaults to database-level
diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt
index d48365dc9e..255584c68b 100644
--- a/docs/topics/db/transactions.txt
+++ b/docs/topics/db/transactions.txt
@@ -22,6 +22,7 @@ integrity of ORM operations that require multiple queries, especially
` queries.
.. versionchanged:: 1.6
+
Previous version of Django featured :ref:`a more complicated default
behavior `.
@@ -73,11 +74,12 @@ To disable this behavior for a specific view, you must set the
In practice, this feature simply wraps every view function in the :func:`atomic`
decorator described below.
-Note that only the execution of your view in enclosed in the transactions.
-Middleware run outside of the transaction, and so does the rendering of
+Note that only the execution of your view is enclosed in the transactions.
+Middleware runs outside of the transaction, and so does the rendering of
template responses.
.. versionchanged:: 1.6
+
Django used to provide this feature via ``TransactionMiddleware``, which is
now deprecated.
@@ -204,6 +206,7 @@ To avoid this, you can :ref:`deactivate the transaction management
`, but it isn't recommended.
.. versionchanged:: 1.6
+
Before Django 1.6, autocommit was turned off, and it was emulated by
forcing a commit after write operations in the ORM.
@@ -224,6 +227,7 @@ where you want to run your own transaction-controlling middleware or do
something really strange.
.. versionchanged:: 1.6
+
This used to be controlled by the ``TRANSACTIONS_MANAGED`` setting.
Low-level APIs
@@ -312,10 +316,10 @@ rollback that would be performed by ``transaction.rollback()``.
.. versionchanged:: 1.6
-When the :func:`atomic` decorator is nested, it creates a savepoint to allow
-partial commit or rollback. You're strongly encouraged to use :func:`atomic`
-rather than the functions described below, but they're still part of the
-public API, and there's no plan to deprecate them.
+ When the :func:`atomic` decorator is nested, it creates a savepoint to allow
+ partial commit or rollback. You're strongly encouraged to use :func:`atomic`
+ rather than the functions described below, but they're still part of the
+ public API, and there's no plan to deprecate them.
Each of these functions takes a ``using`` argument which should be the name of
a database for which the behavior applies. If no ``using`` argument is
@@ -354,20 +358,20 @@ The following example demonstrates the use of savepoints::
@transaction.atomic
def viewfunc(request):
- a.save()
- # transaction now contains a.save()
+ a.save()
+ # transaction now contains a.save()
- sid = transaction.savepoint()
+ sid = transaction.savepoint()
- b.save()
- # transaction now contains a.save() and b.save()
+ b.save()
+ # transaction now contains a.save() and b.save()
- if want_to_keep_b:
- transaction.savepoint_commit(sid)
- # open transaction still contains a.save() and b.save()
- else:
- transaction.savepoint_rollback(sid)
- # open transaction now contains only a.save()
+ if want_to_keep_b:
+ transaction.savepoint_commit(sid)
+ # open transaction still contains a.save() and b.save()
+ else:
+ transaction.savepoint_rollback(sid)
+ # open transaction now contains only a.save()
Database-specific notes
=======================
diff --git a/docs/topics/files.txt b/docs/topics/files.txt
index c05f98ef7e..fb3cdd4af9 100644
--- a/docs/topics/files.txt
+++ b/docs/topics/files.txt
@@ -94,7 +94,7 @@ The following approach may be used to close files automatically::
True
Closing files is especially important when accessing file fields in a loop
-over a large number of objects:: If files are not manually closed after
+over a large number of objects. If files are not manually closed after
accessing them, the risk of running out of file descriptors may arise. This
may lead to the following error::
diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt
index 269ac5b4b6..9d77cd5274 100644
--- a/docs/topics/forms/formsets.txt
+++ b/docs/topics/forms/formsets.txt
@@ -111,6 +111,7 @@ affect validation. If ``validate_max=True`` is passed to the
validation. See :ref:`validate_max`.
.. versionchanged:: 1.6
+
The ``validate_max`` parameter was added to
:func:`~django.forms.formsets.formset_factory`. Also, the behavior of
``FormSet`` was brought in line with that of ``ModelFormSet`` so that it
@@ -310,6 +311,7 @@ should use custom formset validation.
using forged POST requests.
.. versionchanged:: 1.6
+
The ``validate_max`` parameter was added to
:func:`~django.forms.formsets.formset_factory`.
diff --git a/docs/topics/forms/media.txt b/docs/topics/forms/media.txt
index 98e70e5e77..c0d63bb8cf 100644
--- a/docs/topics/forms/media.txt
+++ b/docs/topics/forms/media.txt
@@ -146,7 +146,7 @@ basic Calendar widget from the example above::
-The FancyCalendar widget inherits all the media from it's parent widget. If
+The FancyCalendar widget inherits all the media from its parent widget. If
you don't want media to be inherited in this way, add an ``extend=False``
declaration to the media declaration::
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index eaf2bbbaf2..e58dade736 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -28,6 +28,7 @@ For example::
>>> class ArticleForm(ModelForm):
... class Meta:
... model = Article
+ ... fields = ['pub_date', 'headline', 'content', 'reporter']
# Creating a form to add an article.
>>> form = ArticleForm()
@@ -39,11 +40,13 @@ For example::
Field types
-----------
-The generated ``Form`` class will have a form field for every model field. Each
-model field has a corresponding default form field. For example, a
-``CharField`` on a model is represented as a ``CharField`` on a form. A
-model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is
-the full list of conversions:
+The generated ``Form`` class will have a form field for every model field
+specified, in the order specified in the ``fields`` attribute.
+
+Each model field has a corresponding default form field. For example, a
+``CharField`` on a model is represented as a ``CharField`` on a form. A model
+``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is the
+full list of conversions:
=============================== ========================================
Model field Form field
@@ -168,10 +171,13 @@ Consider this set of models::
class AuthorForm(ModelForm):
class Meta:
model = Author
+ fields = ['name', 'title', 'birth_date']
class BookForm(ModelForm):
class Meta:
model = Book
+ fields = ['name', 'authors']
+
With these models, the ``ModelForm`` subclasses above would be roughly
equivalent to this (the only difference being the ``save()`` method, which
@@ -288,47 +294,66 @@ method is used to determine whether a form requires multipart file upload (and
hence whether ``request.FILES`` must be passed to the form), etc. See
:ref:`binding-uploaded-files` for more information.
-Using a subset of fields on the form
-------------------------------------
+.. _modelforms-selecting-fields:
-In some cases, you may not want all the model fields to appear on the generated
-form. There are three ways of telling ``ModelForm`` to use only a subset of the
-model fields:
+Selecting the fields to use
+---------------------------
-1. Set ``editable=False`` on the model field. As a result, *any* form
- created from the model via ``ModelForm`` will not include that
- field.
+It is strongly recommended that you explicitly set all fields that should be
+edited in the form using the ``fields`` attribute. Failure to do so can easily
+lead to security problems when a form unexpectedly allows a user to set certain
+fields, especially when new fields are added to a model. Depending on how the
+form is rendered, the problem may not even be visible on the web page.
-2. Use the ``fields`` attribute of the ``ModelForm``'s inner ``Meta``
- class. This attribute, if given, should be a list of field names
- to include in the form. The order in which the fields names are specified
- in that list is respected when the form renders them.
+The alternative approach would be to include all fields automatically, or
+blacklist only some. This fundamental approach is known to be much less secure
+and has led to serious exploits on major websites (e.g. `GitHub
+`_).
-3. Use the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta``
- class. This attribute, if given, should be a list of field names
- to exclude from the form.
+There are, however, two shortcuts available for cases where you can guarantee
+these security concerns do not apply to you:
-For example, if you want a form for the ``Author`` model (defined
-above) that includes only the ``name`` and ``birth_date`` fields, you would
-specify ``fields`` or ``exclude`` like this::
+1. Set the ``fields`` attribute to the special value ``'__all__'`` to indicate
+ that all fields in the model should be used. For example::
- class PartialAuthorForm(ModelForm):
- class Meta:
- model = Author
- fields = ('name', 'birth_date')
+ class AuthorForm(ModelForm):
+ class Meta:
+ model = Author
+ fields = '__all__'
- class PartialAuthorForm(ModelForm):
- class Meta:
- model = Author
- exclude = ('title',)
+2. Set the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` class to
+ a list of fields to be excluded from the form.
+
+ For example::
+
+ class PartialAuthorForm(ModelForm):
+ class Meta:
+ model = Author
+ exclude = ['title']
+
+ Since the ``Author`` model has the 3 fields ``name``, ``title`` and
+ ``birth_date``, this will result in the fields ``name`` and ``birth_date``
+ being present on the form.
+
+If either of these are used, the order the fields appear in the form will be the
+order the fields are defined in the model, with ``ManyToManyField`` instances
+appearing last.
+
+In addition, Django applies the following rule: if you set ``editable=False`` on
+the model field, *any* form created from the model via ``ModelForm`` will not
+include that field.
+
+.. versionchanged:: 1.6
+
+ Before version 1.6, the ``'__all__'`` shortcut did not exist, but omitting
+ the ``fields`` attribute had the same effect. Omitting both ``fields`` and
+ ``exclude`` is now deprecated, but will continue to work as before until
+ version 1.8
-Since the Author model has only 3 fields, 'name', 'title', and
-'birth_date', the forms above will contain exactly the same fields.
.. note::
- If you specify ``fields`` or ``exclude`` when creating a form with
- ``ModelForm``, then the fields that are not in the resulting form
+ Any fields not included in a form by the above logic
will not be set by the form's ``save()`` method. Also, if you
manually add the excluded fields back to the form, they will not
be initialized from the model instance.
@@ -401,15 +426,19 @@ field, you could do the following::
class Meta:
model = Article
+ fields = ['pub_date', 'headline', 'content', 'reporter']
+
If you want to override a field's default label, then specify the ``label``
parameter when declaring the form field::
- >>> class ArticleForm(ModelForm):
- ... pub_date = DateField(label='Publication date')
- ...
- ... class Meta:
- ... model = Article
+ class ArticleForm(ModelForm):
+ pub_date = DateField(label='Publication date')
+
+ class Meta:
+ model = Article
+ fields = ['pub_date', 'headline', 'content', 'reporter']
+
.. note::
@@ -436,6 +465,7 @@ parameter when declaring the form field::
class Meta:
model = Article
+ fields = ['headline', 'content']
You must ensure that the type of the form field can be used to set the
contents of the corresponding model field. When they are not compatible,
@@ -444,30 +474,6 @@ parameter when declaring the form field::
See the :doc:`form field documentation ` for more information
on fields and their arguments.
-Changing the order of fields
-----------------------------
-
-By default, a ``ModelForm`` will render fields in the same order that they are
-defined on the model, with ``ManyToManyField`` instances appearing last. If
-you want to change the order in which fields are rendered, you can use the
-``fields`` attribute on the ``Meta`` class.
-
-The ``fields`` attribute defines the subset of model fields that will be
-rendered, and the order in which they will be rendered. For example given this
-model::
-
- class Book(models.Model):
- author = models.ForeignKey(Author)
- title = models.CharField(max_length=100)
-
-the ``author`` field would be rendered first. If we wanted the title field
-to be rendered first, we could specify the following ``ModelForm``::
-
- >>> class BookForm(ModelForm):
- ... class Meta:
- ... model = Book
- ... fields = ('title', 'author')
-
.. _overriding-modelform-clean-method:
Overriding the clean() method
@@ -550,21 +556,19 @@ definition. This may be more convenient if you do not have many customizations
to make::
>>> from django.forms.models import modelform_factory
- >>> BookForm = modelform_factory(Book)
+ >>> BookForm = modelform_factory(Book, fields=("author", "title"))
This can also be used to make simple modifications to existing forms, for
-example by specifying which fields should be displayed::
-
- >>> Form = modelform_factory(Book, form=BookForm, fields=("author",))
-
-... or which fields should be excluded::
-
- >>> Form = modelform_factory(Book, form=BookForm, exclude=("title",))
-
-You can also specify the widgets to be used for a given field::
+example by specifying the widgets to be used for a given field::
>>> from django.forms import Textarea
- >>> Form = modelform_factory(Book, form=BookForm, widgets={"title": Textarea()})
+ >>> Form = modelform_factory(Book, form=BookForm,
+ widgets={"title": Textarea()})
+
+The fields to include can be specified using the ``fields`` and ``exclude``
+keyword arguments, or the corresponding attributes on the ``ModelForm`` inner
+``Meta`` class. Please see the ``ModelForm`` :ref:`modelforms-selecting-fields`
+documentation.
.. _model-formsets:
@@ -688,11 +692,10 @@ database. If a given instance's data didn't change in the bound data, the
instance won't be saved to the database and won't be included in the return
value (``instances``, in the above example).
-When fields are missing from the form (for example because they have
-been excluded), these fields will not be set by the ``save()``
-method. You can find more information about this restriction, which
-also holds for regular ``ModelForms``, in `Using a subset of fields on
-the form`_.
+When fields are missing from the form (for example because they have been
+excluded), these fields will not be set by the ``save()`` method. You can find
+more information about this restriction, which also holds for regular
+``ModelForms``, in `Selecting the fields to use`_.
Pass ``commit=False`` to return the unsaved model instances::
diff --git a/docs/topics/http/middleware.txt b/docs/topics/http/middleware.txt
index 18243c77ce..503d4322e0 100644
--- a/docs/topics/http/middleware.txt
+++ b/docs/topics/http/middleware.txt
@@ -204,6 +204,7 @@ Dealing with streaming responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionchanged:: 1.5
+
``response`` may also be an :class:`~django.http.StreamingHttpResponse`
object.
diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt
index f5c688e254..acad61eb2a 100644
--- a/docs/topics/http/sessions.txt
+++ b/docs/topics/http/sessions.txt
@@ -71,6 +71,7 @@ default cache. To use another cache, set :setting:`SESSION_CACHE_ALIAS` to the
name of that cache.
.. versionchanged:: 1.5
+
The :setting:`SESSION_CACHE_ALIAS` setting was added.
Once your cache is configured, you've got two choices for how to store data in
@@ -451,6 +452,7 @@ Similarly, the ``expires`` part of a session cookie is updated each time the
session cookie is sent.
.. versionchanged:: 1.5
+
The session is not saved if the response's status code is 500.
.. _browser-length-vs-persistent-sessions:
diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt
index 961f0b9d96..52a2935977 100644
--- a/docs/topics/http/shortcuts.txt
+++ b/docs/topics/http/shortcuts.txt
@@ -51,6 +51,7 @@ Optional arguments
the :setting:`DEFAULT_CONTENT_TYPE` setting.
.. versionchanged:: 1.5
+
This parameter used to be called ``mimetype``.
``status``
@@ -129,6 +130,7 @@ Optional arguments
the :setting:`DEFAULT_CONTENT_TYPE` setting.
.. versionchanged:: 1.5
+
This parameter used to be called ``mimetype``.
diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt
index 811425d229..2ce9d8d2bc 100644
--- a/docs/topics/i18n/translation.txt
+++ b/docs/topics/i18n/translation.txt
@@ -1030,11 +1030,11 @@ prepend the current active language code to all url patterns defined within
from django.conf.urls import patterns, include, url
from django.conf.urls.i18n import i18n_patterns
- urlpatterns = patterns(''
+ urlpatterns = patterns('',
url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'),
)
- news_patterns = patterns(''
+ news_patterns = patterns('',
url(r'^$', 'news.views.index', name='index'),
url(r'^category/(?P[\w-]+)/$', 'news.views.category', name='category'),
url(r'^(?P[\w-]+)/$', 'news.views.details', name='detail'),
@@ -1529,6 +1529,7 @@ selection based on data from the request. It customizes content for each user.
``'django.middleware.locale.LocaleMiddleware'``.
.. versionchanged:: 1.6
+
In previous versions, ``LocaleMiddleware`` wasn't enabled by default.
Because middleware order matters, you should follow these guidelines:
diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
index cb22a57e84..a31dc01cc5 100644
--- a/docs/topics/logging.txt
+++ b/docs/topics/logging.txt
@@ -169,7 +169,7 @@ issued on the ``project.interesting`` and
``project.interesting.stuff`` loggers.
This propagation can be controlled on a per-logger basis. If
-you don't want a particular logger to propagate to it's parents, you
+you don't want a particular logger to propagate to its parents, you
can turn off this behavior.
Making logging calls
diff --git a/docs/topics/pagination.txt b/docs/topics/pagination.txt
index 17747c22ff..9da71563c3 100644
--- a/docs/topics/pagination.txt
+++ b/docs/topics/pagination.txt
@@ -252,7 +252,7 @@ Methods
.. versionchanged:: 1.5
- Raises :exc:`InvalidPage` if next page doesn't exist.
+ Raises :exc:`InvalidPage` if next page doesn't exist.
.. method:: Page.previous_page_number()
@@ -260,7 +260,7 @@ Methods
.. versionchanged:: 1.5
- Raises :exc:`InvalidPage` if previous page doesn't exist.
+ Raises :exc:`InvalidPage` if previous page doesn't exist.
.. method:: Page.start_index()
diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt
index 33f5fcd4c0..22e609c75c 100644
--- a/docs/topics/python3.txt
+++ b/docs/topics/python3.txt
@@ -317,6 +317,9 @@ Division
def __idiv__(self, other): # Python 2 compatibility
return type(self).__itruediv__(self, other)
+Special methods are looked up on the class and not on the instance to reflect
+the behavior of the Python interpreter.
+
.. module: django.utils.six
Writing compatible code with six
diff --git a/docs/topics/security.txt b/docs/topics/security.txt
index 566202eefa..22135a72ea 100644
--- a/docs/topics/security.txt
+++ b/docs/topics/security.txt
@@ -168,7 +168,7 @@ certain cases. While these values are sanitized to prevent Cross Site Scripting
attacks, a fake ``Host`` value can be used for Cross-Site Request Forgery,
cache poisoning attacks, and poisoning links in emails.
-Because even seemingly-secure webserver configurations are susceptible to fake
+Because even seemingly-secure web server configurations are susceptible to fake
``Host`` headers, Django validates ``Host`` headers against the
:setting:`ALLOWED_HOSTS` setting in the
:meth:`django.http.HttpRequest.get_host()` method.
@@ -181,15 +181,15 @@ For more details see the full :setting:`ALLOWED_HOSTS` documentation.
.. warning::
- Previous versions of this document recommended configuring your webserver to
+ Previous versions of this document recommended configuring your web server to
ensure it validates incoming HTTP ``Host`` headers. While this is still
- recommended, in many common webservers a configuration that seems to
+ recommended, in many common web servers a configuration that seems to
validate the ``Host`` header may not in fact do so. For instance, even if
Apache is configured such that your Django site is served from a non-default
virtual host with the ``ServerName`` set, it is still possible for an HTTP
request to match this virtual host and supply a fake ``Host`` header. Thus,
Django now requires that you set :setting:`ALLOWED_HOSTS` explicitly rather
- than relying on webserver configuration.
+ than relying on web server configuration.
Additionally, as of 1.3.1, Django requires you to explicitly enable support for
the ``X-Forwarded-Host`` header (via the :setting:`USE_X_FORWARDED_HOST`
diff --git a/docs/topics/serialization.txt b/docs/topics/serialization.txt
index ce39f6cd28..cb34117997 100644
--- a/docs/topics/serialization.txt
+++ b/docs/topics/serialization.txt
@@ -124,8 +124,8 @@ Calling ``DeserializedObject.save()`` saves the object to the database.
.. versionchanged:: 1.6
-In previous versions of Django, the ``pk`` attribute had to be present
-on the serialized data or a ``DeserializationError`` would be raised.
+ In previous versions of Django, the ``pk`` attribute had to be present
+ on the serialized data or a ``DeserializationError`` would be raised.
This ensures that deserializing is a non-destructive operation even if the
data in your serialized representation doesn't match what's currently in the
@@ -144,11 +144,11 @@ The Django object itself can be inspected as ``deserialized_object.object``.
.. versionadded:: 1.5
-If fields in the serialized data do not exist on a model,
-a ``DeserializationError`` will be raised unless the ``ignorenonexistent``
-argument is passed in as True::
+ If fields in the serialized data do not exist on a model,
+ a ``DeserializationError`` will be raised unless the ``ignorenonexistent``
+ argument is passed in as True::
- serializers.deserialize("xml", data, ignorenonexistent=True)
+ serializers.deserialize("xml", data, ignorenonexistent=True)
.. _serialization-formats:
diff --git a/docs/topics/signals.txt b/docs/topics/signals.txt
index d611da4a37..a97fb2f14f 100644
--- a/docs/topics/signals.txt
+++ b/docs/topics/signals.txt
@@ -134,7 +134,7 @@ to.
.. versionchanged:: 1.5
-The ability to pass a list of signals was added.
+ The ability to pass a list of signals was added.
.. admonition:: Where should this code live?
diff --git a/docs/topics/testing/advanced.txt b/docs/topics/testing/advanced.txt
index 26dc8ee1ae..5f2fa65bed 100644
--- a/docs/topics/testing/advanced.txt
+++ b/docs/topics/testing/advanced.txt
@@ -163,10 +163,12 @@ environment first. Django provides a convenience method to do this::
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
-This convenience method sets up the test database, and puts other
-Django features into modes that allow for repeatable testing.
+:func:`~django.test.utils.setup_test_environment` puts several Django features
+into modes that allow for repeatable testing, but does not create the test
+databases; :func:`django.test.simple.DjangoTestSuiteRunner.setup_databases`
+takes care of that.
-The call to :meth:`~django.test.utils.setup_test_environment` is made
+The call to :func:`~django.test.utils.setup_test_environment` is made
automatically as part of the setup of ``./manage.py test``. You only
need to manually invoke this method if you're not using running your
tests via Django's test runner.
@@ -282,7 +284,9 @@ Methods
.. method:: DjangoTestSuiteRunner.setup_test_environment(**kwargs)
- Sets up the test environment ready for testing.
+ Sets up the test environment by calling
+ :func:`~django.test.utils.setup_test_environment` and setting
+ :setting:`DEBUG` to ``False``.
.. method:: DjangoTestSuiteRunner.build_suite(test_labels, extra_tests=None, **kwargs)
@@ -340,6 +344,9 @@ Methods
Testing utilities
-----------------
+django.test.utils
+~~~~~~~~~~~~~~~~~
+
.. module:: django.test.utils
:synopsis: Helpers to write custom test runners.
@@ -358,10 +365,13 @@ utility methods in the ``django.test.utils`` module.
magic hooks into the template system and restoring normal email
services.
+django.db.connection.creation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
.. currentmodule:: django.db.connection.creation
-The creation module of the database backend (``connection.creation``)
-also provides some utilities that can be useful during testing.
+The creation module of the database backend also provides some utilities that
+can be useful during testing.
.. function:: create_test_db([verbosity=1, autoclobber=False])
diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt
index 259c39618b..9228a07b31 100644
--- a/docs/topics/testing/overview.txt
+++ b/docs/topics/testing/overview.txt
@@ -25,7 +25,7 @@ module defines tests in class-based approach.
adding some extremely useful features. To ensure that every Django
project can benefit from these new features, Django ships with a
copy of unittest2_, a copy of the Python 2.7 unittest library,
- backported for Python 2.5 compatibility.
+ backported for Python 2.6 compatibility.
To access this library, Django provides the
``django.utils.unittest`` module alias. If you are using Python
@@ -235,6 +235,7 @@ the Django test runner reorders tests in the following way:
restoring it to its original state are run.
.. versionchanged:: 1.5
+
Before Django 1.5, the only guarantee was that
:class:`~django.test.TestCase` tests were always ran first, before any other
tests.
@@ -612,6 +613,7 @@ Use the ``django.test.client.Client`` class to make requests.
a ``Content-Type`` header is set to ``content_type``.
.. versionchanged:: 1.5
+
:meth:`Client.options` used to process ``data`` like
:meth:`Client.get`.
@@ -627,6 +629,7 @@ Use the ``django.test.client.Client`` class to make requests.
a ``Content-Type`` header is set to ``content_type``.
.. versionchanged:: 1.5
+
:meth:`Client.put` used to process ``data`` like
:meth:`Client.post`.
@@ -650,6 +653,7 @@ Use the ``django.test.client.Client`` class to make requests.
a ``Content-Type`` header is set to ``content_type``.
.. versionchanged:: 1.5
+
:meth:`Client.delete` used to process ``data`` like
:meth:`Client.get`.
@@ -940,6 +944,7 @@ to test the effects of commit and rollback:
the test has been properly updated.
.. versionchanged:: 1.5
+
The order in which tests are run has changed. See `Order in which tests are
executed`_.
@@ -990,6 +995,7 @@ additions, including:
errors.
.. versionchanged:: 1.5
+
The order in which tests are run has changed. See `Order in which tests are
executed`_.
@@ -1581,6 +1587,7 @@ your test suite.
``False``, which turns the comparison into a Python set comparison.
.. versionchanged:: 1.6
+
The method now checks for undefined order and raises ``ValueError``
if undefined order is spotted. The ordering is seen as undefined if
the given ``qs`` isn't ordered and the comparison is against more
diff --git a/extras/Makefile b/extras/Makefile
new file mode 100644
index 0000000000..ff14f404e2
--- /dev/null
+++ b/extras/Makefile
@@ -0,0 +1,9 @@
+all: sdist bdist_wheel
+
+sdist:
+ python setup.py sdist
+
+bdist_wheel:
+ python -c "import setuptools;__file__='setup.py';exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))" bdist_wheel
+
+.PHONY : sdist bdist_wheel
diff --git a/scripts/manage_translations.py b/scripts/manage_translations.py
index b90b26e1f8..3e58040187 100644
--- a/scripts/manage_translations.py
+++ b/scripts/manage_translations.py
@@ -47,9 +47,9 @@ def _get_locale_dirs(include_core=True):
def _tx_resource_for_name(name):
""" Return the Transifex resource name """
if name == 'core':
- return "django.core"
+ return "django-core.core"
else:
- return "django.contrib-%s" % name
+ return "django-core.contrib-%s" % name
def _check_diff(cat_name, base_path):
"""
diff --git a/setup.cfg b/setup.cfg
index e189ffb47b..330eff6977 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,3 +2,8 @@
doc_files = docs extras AUTHORS INSTALL LICENSE README.rst
install-script = scripts/rpm-install.sh
+[metadata]
+license-file = LICENSE
+
+[wheel]
+universal = 1 # use py2.py3 tag for pure-python dist
diff --git a/setup.py b/setup.py
index 006cba4137..7f848a56ff 100644
--- a/setup.py
+++ b/setup.py
@@ -1,10 +1,9 @@
-from distutils.core import setup
-from distutils.command.install_data import install_data
-from distutils.command.install import INSTALL_SCHEMES
-from distutils.sysconfig import get_python_lib
import os
import sys
+from distutils.core import setup
+from distutils.sysconfig import get_python_lib
+
# Warn if we are installing over top of an existing installation. This can
# cause issues where files that were deleted from a more recent Django are
# still present in site-packages. See #18115.
@@ -23,28 +22,11 @@ if "install" in sys.argv:
overlay_warning = True
break
-class osx_install_data(install_data):
- # On MacOS, the platform-specific lib dir is /System/Library/Framework/Python/.../
- # which is wrong. Python 2.5 supplied with MacOS 10.5 has an Apple-specific fix
- # for this in distutils.command.install_data#306. It fixes install_lib but not
- # install_data, which is why we roll our own install_data class.
-
- def finalize_options(self):
- # By the time finalize_options is called, install.install_lib is set to the
- # fixed directory, so we set the installdir to install_lib. The
- # install_data class uses ('install_data', 'install_dir') instead.
- self.set_undefined_options('install', ('install_lib', 'install_dir'))
- install_data.finalize_options(self)
-
-if sys.platform == "darwin":
- cmdclasses = {'install_data': osx_install_data}
-else:
- cmdclasses = {'install_data': install_data}
def fullsplit(path, result=None):
"""
- Split a pathname into components (the opposite of os.path.join) in a
- platform-neutral way.
+ Split a pathname into components (the opposite of os.path.join)
+ in a platform-neutral way.
"""
if result is None:
result = []
@@ -55,15 +37,23 @@ def fullsplit(path, result=None):
return result
return fullsplit(head, [tail] + result)
-# Tell distutils not to put the data_files in platform-specific installation
-# locations. See here for an explanation:
-# http://groups.google.com/group/comp.lang.python/browse_thread/thread/35ec7b2fed36eaec/2105ee4d9e8042cb
-for scheme in INSTALL_SCHEMES.values():
- scheme['data'] = scheme['purelib']
+
+EXCLUDE_FROM_PACKAGES = ['django.conf.project_template',
+ 'django.conf.app_template',
+ 'django.bin']
+
+
+def is_package(package_name):
+ for pkg in EXCLUDE_FROM_PACKAGES:
+ if package_name.startswith(pkg):
+ return False
+ return True
+
# Compile the list of packages available, because distutils doesn't have
# an easy way to do this.
-packages, data_files = [], []
+packages, package_data = [], {}
+
root_dir = os.path.dirname(__file__)
if root_dir != '':
os.chdir(root_dir)
@@ -72,33 +62,37 @@ django_dir = 'django'
for dirpath, dirnames, filenames in os.walk(django_dir):
# Ignore PEP 3147 cache dirs and those whose names start with '.'
dirnames[:] = [d for d in dirnames if not d.startswith('.') and d != '__pycache__']
- if '__init__.py' in filenames:
- packages.append('.'.join(fullsplit(dirpath)))
+ parts = fullsplit(dirpath)
+ package_name = '.'.join(parts)
+ if '__init__.py' in filenames and is_package(package_name):
+ packages.append(package_name)
elif filenames:
- data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]])
+ relative_path = []
+ while '.'.join(parts) not in packages:
+ relative_path.append(parts.pop())
+ relative_path.reverse()
+ path = os.path.join(*relative_path)
+ package_files = package_data.setdefault('.'.join(parts), [])
+ package_files.extend([os.path.join(path, f) for f in filenames])
-# Small hack for working with bdist_wininst.
-# See http://mail.python.org/pipermail/distutils-sig/2004-August/004134.html
-if len(sys.argv) > 1 and sys.argv[1] == 'bdist_wininst':
- for file_info in data_files:
- file_info[0] = '\\PURELIB\\%s' % file_info[0]
# Dynamically calculate the version based on django.VERSION.
version = __import__('django').get_version()
+
setup(
- name = "Django",
- version = version,
- url = 'http://www.djangoproject.com/',
- author = 'Django Software Foundation',
- author_email = 'foundation@djangoproject.com',
- description = 'A high-level Python Web framework that encourages rapid development and clean, pragmatic design.',
- license = "BSD",
- packages = packages,
- cmdclass = cmdclasses,
- data_files = data_files,
- scripts = ['django/bin/django-admin.py'],
- classifiers = [
+ name='Django',
+ version=version,
+ url='http://www.djangoproject.com/',
+ author='Django Software Foundation',
+ author_email='foundation@djangoproject.com',
+ description=('A high-level Python Web framework that encourages '
+ 'rapid development and clean, pragmatic design.'),
+ license='BSD',
+ packages=packages,
+ package_data=package_data,
+ scripts=['django/bin/django-admin.py'],
+ classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Django',
@@ -115,7 +109,7 @@ setup(
'Topic :: Internet :: WWW/HTTP :: WSGI',
'Topic :: Software Development :: Libraries :: Application Frameworks',
'Topic :: Software Development :: Libraries :: Python Modules',
- ],
+ ],
)
if overlay_warning:
@@ -136,4 +130,4 @@ should manually remove the
directory and re-install Django.
-""" % { "existing_path": existing_path })
+""" % {"existing_path": existing_path})
diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py
index baec16820e..5071977b2d 100644
--- a/tests/admin_scripts/tests.py
+++ b/tests/admin_scripts/tests.py
@@ -14,7 +14,8 @@ import subprocess
import sys
import codecs
-from django import conf, bin, get_version
+import django
+from django import conf, get_version
from django.conf import settings
from django.core.management import BaseCommand, CommandError
from django.db import connection
@@ -149,8 +150,8 @@ class AdminScriptTestCase(unittest.TestCase):
return out, err
def run_django_admin(self, args, settings_file=None):
- bin_dir = os.path.abspath(os.path.dirname(upath(bin.__file__)))
- return self.run_test(os.path.join(bin_dir, 'django-admin.py'), args, settings_file)
+ script_dir = os.path.abspath(os.path.join(os.path.dirname(upath(django.__file__)), 'bin'))
+ return self.run_test(os.path.join(script_dir, 'django-admin.py'), args, settings_file)
def run_manage(self, args, settings_file=None):
def safe_remove(path):
diff --git a/tests/admin_validation/tests.py b/tests/admin_validation/tests.py
index 5b2c45f6f2..16f73c6390 100644
--- a/tests/admin_validation/tests.py
+++ b/tests/admin_validation/tests.py
@@ -16,10 +16,6 @@ class ValidFields(admin.ModelAdmin):
form = SongForm
fields = ['title']
-class InvalidFields(admin.ModelAdmin):
- form = SongForm
- fields = ['spam']
-
class ValidFormFieldsets(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
class ExtraFieldForm(SongForm):
@@ -49,10 +45,6 @@ class ValidationTestCase(TestCase):
# Regression test for #8027: custom ModelForms with fields/fieldsets
"""
validate(ValidFields, Song)
- self.assertRaisesMessage(ImproperlyConfigured,
- "'InvalidFields.fields' refers to field 'spam' that is missing from the form.",
- validate,
- InvalidFields, Song)
def test_custom_get_form_with_fieldsets(self):
"""
@@ -277,8 +269,6 @@ class ValidationTestCase(TestCase):
"""
class SongForm(forms.ModelForm):
extra_data = forms.CharField()
- class Meta:
- model = Song
class FieldsOnFormOnlyAdmin(admin.ModelAdmin):
form = SongForm
@@ -295,6 +285,8 @@ class ValidationTestCase(TestCase):
extra_data = forms.CharField()
class Meta:
model = Song
+ fields = '__all__'
+
class FieldsOnFormOnlyAdmin(admin.ModelAdmin):
form = SongForm
diff --git a/tests/bug639/models.py b/tests/bug639/models.py
index e641555c87..fa8e7d2c07 100644
--- a/tests/bug639/models.py
+++ b/tests/bug639/models.py
@@ -25,3 +25,4 @@ class Photo(models.Model):
class PhotoForm(ModelForm):
class Meta:
model = Photo
+ fields = '__all__'
diff --git a/tests/foreign_object/tests.py b/tests/foreign_object/tests.py
index 2ca13cb786..55dd6a0f47 100644
--- a/tests/foreign_object/tests.py
+++ b/tests/foreign_object/tests.py
@@ -322,6 +322,7 @@ class FormsTests(TestCase):
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
+ fields = '__all__'
def test_foreign_object_form(self):
# A very crude test checking that the non-concrete fields do not get form fields.
diff --git a/tests/forms_tests/tests/test_regressions.py b/tests/forms_tests/tests/test_regressions.py
index be9dc8c593..74509a0f1a 100644
--- a/tests/forms_tests/tests/test_regressions.py
+++ b/tests/forms_tests/tests/test_regressions.py
@@ -139,6 +139,7 @@ class FormsRegressionsTestCase(TestCase):
class CheeseForm(ModelForm):
class Meta:
model = Cheese
+ fields = '__all__'
form = CheeseForm({
'name': 'Brie',
diff --git a/tests/forms_tests/tests/tests.py b/tests/forms_tests/tests/tests.py
index 80e2cadcc3..deda4822b8 100644
--- a/tests/forms_tests/tests/tests.py
+++ b/tests/forms_tests/tests/tests.py
@@ -17,11 +17,13 @@ from ..models import (ChoiceOptionModel, ChoiceFieldModel, FileModel, Group,
class ChoiceFieldForm(ModelForm):
class Meta:
model = ChoiceFieldModel
+ fields = '__all__'
class OptionalMultiChoiceModelForm(ModelForm):
class Meta:
model = OptionalMultiChoiceModel
+ fields = '__all__'
class FileForm(Form):
@@ -139,6 +141,7 @@ class FormsModelTestCase(TestCase):
class BoundaryForm(ModelForm):
class Meta:
model = BoundaryModel
+ fields = '__all__'
f = BoundaryForm({'positive_integer': 100})
self.assertTrue(f.is_valid())
@@ -154,6 +157,7 @@ class FormsModelTestCase(TestCase):
class DefaultsForm(ModelForm):
class Meta:
model = Defaults
+ fields = '__all__'
self.assertEqual(DefaultsForm().fields['name'].initial, 'class default value')
self.assertEqual(DefaultsForm().fields['def_date'].initial, datetime.date(1980, 1, 1))
diff --git a/tests/generic_relations/tests.py b/tests/generic_relations/tests.py
index 27b25185ea..dd9dc506ca 100644
--- a/tests/generic_relations/tests.py
+++ b/tests/generic_relations/tests.py
@@ -247,6 +247,7 @@ class CustomWidget(forms.TextInput):
class TaggedItemForm(forms.ModelForm):
class Meta:
model = TaggedItem
+ fields = '__all__'
widgets = {'tag': CustomWidget}
class GenericInlineFormsetTest(TestCase):
diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py
index c54d632363..54eab7ffa4 100644
--- a/tests/generic_views/test_edit.py
+++ b/tests/generic_views/test_edit.py
@@ -1,12 +1,14 @@
from __future__ import absolute_import
+import warnings
+
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django import forms
from django.test import TestCase
from django.utils.unittest import expectedFailure
from django.views.generic.base import View
-from django.views.generic.edit import FormMixin
+from django.views.generic.edit import FormMixin, CreateView, UpdateView
from . import views
from .models import Artist, Author
@@ -34,6 +36,7 @@ class ModelFormMixinTests(TestCase):
form_class = views.AuthorGetQuerySetFormView().get_form_class()
self.assertEqual(form_class._meta.model, Author)
+
class CreateViewTests(TestCase):
urls = 'generic_views.urls'
@@ -112,6 +115,45 @@ class CreateViewTests(TestCase):
self.assertEqual(res.status_code, 302)
self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/')
+ def test_create_view_with_restricted_fields(self):
+
+ class MyCreateView(CreateView):
+ model = Author
+ fields = ['name']
+
+ self.assertEqual(list(MyCreateView().get_form_class().base_fields),
+ ['name'])
+
+ def test_create_view_all_fields(self):
+
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always", PendingDeprecationWarning)
+
+ class MyCreateView(CreateView):
+ model = Author
+ fields = '__all__'
+
+ self.assertEqual(list(MyCreateView().get_form_class().base_fields),
+ ['name', 'slug'])
+ self.assertEqual(len(w), 0)
+
+
+ def test_create_view_without_explicit_fields(self):
+
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always", PendingDeprecationWarning)
+
+ class MyCreateView(CreateView):
+ model = Author
+
+ # Until end of the deprecation cycle, should still create the form
+ # as before:
+ self.assertEqual(list(MyCreateView().get_form_class().base_fields),
+ ['name', 'slug'])
+
+ # but with a warning:
+ self.assertEqual(w[0].category, PendingDeprecationWarning)
+
class UpdateViewTests(TestCase):
urls = 'generic_views.urls'
diff --git a/tests/generic_views/test_forms.py b/tests/generic_views/test_forms.py
index e036ad8afc..8c118e32a6 100644
--- a/tests/generic_views/test_forms.py
+++ b/tests/generic_views/test_forms.py
@@ -11,6 +11,7 @@ class AuthorForm(forms.ModelForm):
class Meta:
model = Author
+ fields = ['name', 'slug']
class ContactForm(forms.Form):
diff --git a/tests/generic_views/views.py b/tests/generic_views/views.py
index 69bfcb32dd..aa8777e8c6 100644
--- a/tests/generic_views/views.py
+++ b/tests/generic_views/views.py
@@ -85,15 +85,18 @@ class ContactView(generic.FormView):
class ArtistCreate(generic.CreateView):
model = Artist
+ fields = '__all__'
class NaiveAuthorCreate(generic.CreateView):
queryset = Author.objects.all()
+ fields = '__all__'
class AuthorCreate(generic.CreateView):
model = Author
success_url = '/list/authors/'
+ fields = '__all__'
class SpecializedAuthorCreate(generic.CreateView):
@@ -112,19 +115,23 @@ class AuthorCreateRestricted(AuthorCreate):
class ArtistUpdate(generic.UpdateView):
model = Artist
+ fields = '__all__'
class NaiveAuthorUpdate(generic.UpdateView):
queryset = Author.objects.all()
+ fields = '__all__'
class AuthorUpdate(generic.UpdateView):
model = Author
success_url = '/list/authors/'
+ fields = '__all__'
class OneAuthorUpdate(generic.UpdateView):
success_url = '/list/authors/'
+ fields = '__all__'
def get_object(self):
return Author.objects.get(pk=1)
@@ -184,6 +191,8 @@ class BookDetail(BookConfig, generic.DateDetailView):
pass
class AuthorGetQuerySetFormView(generic.edit.ModelFormMixin):
+ fields = '__all__'
+
def get_queryset(self):
return Author.objects.all()
diff --git a/tests/get_or_create/models.py b/tests/get_or_create/models.py
index 678f5a401c..82905de4f8 100644
--- a/tests/get_or_create/models.py
+++ b/tests/get_or_create/models.py
@@ -24,3 +24,7 @@ class Person(models.Model):
class ManualPrimaryKeyTest(models.Model):
id = models.IntegerField(primary_key=True)
data = models.CharField(max_length=100)
+
+
+class Profile(models.Model):
+ person = models.ForeignKey(Person, primary_key=True)
diff --git a/tests/get_or_create/tests.py b/tests/get_or_create/tests.py
index 1e300fbb4d..e9cce9bbde 100644
--- a/tests/get_or_create/tests.py
+++ b/tests/get_or_create/tests.py
@@ -4,9 +4,9 @@ from datetime import date
import traceback
from django.db import IntegrityError
-from django.test import TestCase
+from django.test import TestCase, TransactionTestCase
-from .models import Person, ManualPrimaryKeyTest
+from .models import Person, ManualPrimaryKeyTest, Profile
class GetOrCreateTests(TestCase):
@@ -64,3 +64,16 @@ class GetOrCreateTests(TestCase):
formatted_traceback = traceback.format_exc()
self.assertIn('obj.save', formatted_traceback)
+
+class GetOrCreateTransactionTests(TransactionTestCase):
+
+ def test_get_or_create_integrityerror(self):
+ # Regression test for #15117. Requires a TransactionTestCase on
+ # databases that delay integrity checks until the end of transactions,
+ # otherwise the exception is never raised.
+ try:
+ Profile.objects.get_or_create(person=Person(id=1))
+ except IntegrityError:
+ pass
+ else:
+ self.skipTest("This backend does not support integrity checks.")
diff --git a/tests/i18n/commands/extraction.py b/tests/i18n/commands/extraction.py
index 80e4ee0110..7c482e58fb 100644
--- a/tests/i18n/commands/extraction.py
+++ b/tests/i18n/commands/extraction.py
@@ -30,6 +30,10 @@ class ExtractorTests(SimpleTestCase):
return
shutil.rmtree(dname)
+ def rmfile(self, filepath):
+ if os.path.exists(filepath):
+ os.remove(filepath)
+
def tearDown(self):
os.chdir(self.test_dir)
try:
@@ -126,18 +130,22 @@ class BasicExtractorTests(ExtractorTests):
# Check that the temporary file was cleaned up
self.assertFalse(os.path.exists('./templates/template_with_error.tpl.py'))
+ def test_unicode_decode_error(self):
+ os.chdir(self.test_dir)
+ shutil.copyfile('./not_utf8.sample', './not_utf8.txt')
+ self.addCleanup(self.rmfile, os.path.join(self.test_dir, 'not_utf8.txt'))
+ stdout = StringIO()
+ management.call_command('makemessages', locale=LOCALE, stdout=stdout)
+ self.assertIn("UnicodeDecodeError: skipped file not_utf8.txt in .",
+ force_text(stdout.getvalue()))
+
def test_extraction_warning(self):
"""test xgettext warning about multiple bare interpolation placeholders"""
os.chdir(self.test_dir)
shutil.copyfile('./code.sample', './code_sample.py')
+ self.addCleanup(self.rmfile, os.path.join(self.test_dir, 'code_sample.py'))
stdout = StringIO()
- try:
- management.call_command('makemessages', locale=LOCALE, stdout=stdout)
- finally:
- try:
- os.remove('./code_sample.py')
- except OSError:
- pass
+ management.call_command('makemessages', locale=LOCALE, stdout=stdout)
self.assertIn("code_sample.py:4", force_text(stdout.getvalue()))
def test_template_message_context_extractor(self):
diff --git a/tests/i18n/commands/not_utf8.sample b/tests/i18n/commands/not_utf8.sample
new file mode 100644
index 0000000000..6449f52803
--- /dev/null
+++ b/tests/i18n/commands/not_utf8.sample
@@ -0,0 +1 @@
+Copyright (c) 2009 Øyvind Sean Kinsey, oyvind@kinsey.no
\ No newline at end of file
diff --git a/tests/i18n/forms.py b/tests/i18n/forms.py
index abb99f443a..6e4def9c5e 100644
--- a/tests/i18n/forms.py
+++ b/tests/i18n/forms.py
@@ -24,3 +24,4 @@ class CompanyForm(forms.ModelForm):
class Meta:
model = Company
+ fields = '__all__'
diff --git a/tests/inline_formsets/tests.py b/tests/inline_formsets/tests.py
index df682d34ef..ad8a666cb5 100644
--- a/tests/inline_formsets/tests.py
+++ b/tests/inline_formsets/tests.py
@@ -10,7 +10,7 @@ from .models import Poet, Poem, School, Parent, Child
class DeletionTests(TestCase):
def test_deletion(self):
- PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True)
+ PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True, fields="__all__")
poet = Poet.objects.create(name='test')
poem = poet.poem_set.create(name='test poem')
data = {
@@ -32,7 +32,7 @@ class DeletionTests(TestCase):
Make sure that an add form that is filled out, but marked for deletion
doesn't cause validation errors.
"""
- PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True)
+ PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True, fields="__all__")
poet = Poet.objects.create(name='test')
data = {
'poem_set-TOTAL_FORMS': '1',
@@ -60,7 +60,7 @@ class DeletionTests(TestCase):
Make sure that a change form that is filled out, but marked for deletion
doesn't cause validation errors.
"""
- PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True)
+ PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True, fields="__all__")
poet = Poet.objects.create(name='test')
poem = poet.poem_set.create(name='test poem')
data = {
@@ -115,8 +115,8 @@ class InlineFormsetFactoryTest(TestCase):
"""
These should both work without a problem.
"""
- inlineformset_factory(Parent, Child, fk_name='mother')
- inlineformset_factory(Parent, Child, fk_name='father')
+ inlineformset_factory(Parent, Child, fk_name='mother', fields="__all__")
+ inlineformset_factory(Parent, Child, fk_name='father', fields="__all__")
def test_exception_on_unspecified_foreign_key(self):
"""
diff --git a/tests/many_to_one_regress/tests.py b/tests/many_to_one_regress/tests.py
index a9969a7e90..035ba53bff 100644
--- a/tests/many_to_one_regress/tests.py
+++ b/tests/many_to_one_regress/tests.py
@@ -60,10 +60,6 @@ class ManyToOneRegressionTests(TestCase):
self.assertRaises(ValueError, Child, name='xyzzy', parent=None)
self.assertRaises(ValueError, Child.objects.create, name='xyzzy', parent=None)
- # Trying to assign to unbound attribute raises AttributeError
- six.assertRaisesRegex(self, AttributeError, "must be accessed via instance",
- Child.parent.__set__, None, p)
-
# Creation using keyword argument should cache the related object.
p = Parent.objects.get(name="Parent")
c = Child(parent=p)
diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py
index 9af8325040..035a5c2ae3 100644
--- a/tests/model_fields/tests.py
+++ b/tests/model_fields/tests.py
@@ -6,7 +6,7 @@ from decimal import Decimal
from django import test
from django import forms
from django.core.exceptions import ValidationError
-from django.db import models, IntegrityError
+from django.db import connection, models, IntegrityError
from django.db.models.fields.files import FieldFile
from django.utils import six
from django.utils import unittest
@@ -455,6 +455,10 @@ class BinaryFieldTests(test.TestCase):
# Test default value
self.assertEqual(bytes(dm.short_data), b'\x08')
+ if connection.vendor == 'mysql' and six.PY3:
+ # Existing MySQL DB-API drivers fail on binary data.
+ test_set_and_retrieve = unittest.expectedFailure(test_set_and_retrieve)
+
def test_max_length(self):
dm = DataModel(short_data=self.binary_data*4)
self.assertRaises(ValidationError, dm.full_clean)
diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py
index 96c3ecbdce..c5db011404 100644
--- a/tests/model_forms/tests.py
+++ b/tests/model_forms/tests.py
@@ -3,6 +3,7 @@ from __future__ import absolute_import, unicode_literals
import datetime
import os
from decimal import Decimal
+import warnings
from django import forms
from django.core.exceptions import FieldError
@@ -30,19 +31,25 @@ if test_images:
class ImageFileForm(forms.ModelForm):
class Meta:
model = ImageFile
+ fields = '__all__'
+
class OptionalImageFileForm(forms.ModelForm):
class Meta:
model = OptionalImageFile
+ fields = '__all__'
+
class ProductForm(forms.ModelForm):
class Meta:
model = Product
+ fields = '__all__'
class PriceForm(forms.ModelForm):
class Meta:
model = Price
+ fields = '__all__'
class BookForm(forms.ModelForm):
@@ -66,11 +73,13 @@ class ExplicitPKForm(forms.ModelForm):
class PostForm(forms.ModelForm):
class Meta:
model = Post
+ fields = '__all__'
class DerivedPostForm(forms.ModelForm):
class Meta:
model = DerivedPost
+ fields = '__all__'
class CustomAuthorForm(forms.ModelForm):
@@ -78,61 +87,79 @@ class CustomAuthorForm(forms.ModelForm):
class Meta:
model = Author
+ fields = '__all__'
class FlexDatePostForm(forms.ModelForm):
class Meta:
model = FlexibleDatePost
+ fields = '__all__'
class BaseCategoryForm(forms.ModelForm):
class Meta:
model = Category
+ fields = '__all__'
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
+ fields = '__all__'
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
+ fields = '__all__'
+
class PartialArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ('headline','pub_date')
+
class RoykoForm(forms.ModelForm):
class Meta:
model = Author
+ fields = '__all__'
+
class TestArticleForm(forms.ModelForm):
class Meta:
model = Article
+ fields = '__all__'
+
class PartialArticleFormWithSlug(forms.ModelForm):
class Meta:
model = Article
- fields=('headline', 'slug', 'pub_date')
+ fields = ('headline', 'slug', 'pub_date')
+
class ArticleStatusForm(forms.ModelForm):
class Meta:
model = ArticleStatus
+ fields = '__all__'
+
class InventoryForm(forms.ModelForm):
class Meta:
model = Inventory
+ fields = '__all__'
+
class SelectInventoryForm(forms.Form):
items = forms.ModelMultipleChoiceField(Inventory.objects.all(), to_field_name='barcode')
+
class CustomFieldForExclusionForm(forms.ModelForm):
class Meta:
model = CustomFieldForExclusionModel
fields = ['name', 'markup']
+
class ShortCategory(forms.ModelForm):
name = forms.CharField(max_length=5)
slug = forms.CharField(max_length=5)
@@ -140,30 +167,44 @@ class ShortCategory(forms.ModelForm):
class Meta:
model = Category
+ fields = '__all__'
+
class ImprovedArticleForm(forms.ModelForm):
class Meta:
model = ImprovedArticle
+ fields = '__all__'
+
class ImprovedArticleWithParentLinkForm(forms.ModelForm):
class Meta:
model = ImprovedArticleWithParentLink
+ fields = '__all__'
+
class BetterAuthorForm(forms.ModelForm):
class Meta:
model = BetterAuthor
+ fields = '__all__'
+
class AuthorProfileForm(forms.ModelForm):
class Meta:
model = AuthorProfile
+ fields = '__all__'
+
class TextFileForm(forms.ModelForm):
class Meta:
model = TextFile
+ fields = '__all__'
+
class BigIntForm(forms.ModelForm):
class Meta:
model = BigInt
+ fields = '__all__'
+
class ModelFormWithMedia(forms.ModelForm):
class Media:
@@ -173,19 +214,25 @@ class ModelFormWithMedia(forms.ModelForm):
}
class Meta:
model = TextFile
+ fields = '__all__'
+
class CommaSeparatedIntegerForm(forms.ModelForm):
- class Meta:
- model = CommaSeparatedInteger
+ class Meta:
+ model = CommaSeparatedInteger
+ fields = '__all__'
+
class PriceFormWithoutQuantity(forms.ModelForm):
class Meta:
model = Price
exclude = ('quantity',)
+
class ColourfulItemForm(forms.ModelForm):
class Meta:
model = ColourfulItem
+ fields = '__all__'
class ModelFormBaseTest(TestCase):
@@ -193,6 +240,25 @@ class ModelFormBaseTest(TestCase):
self.assertEqual(list(BaseCategoryForm.base_fields),
['name', 'slug', 'url'])
+ def test_missing_fields_attribute(self):
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always", PendingDeprecationWarning)
+
+ class MissingFieldsForm(forms.ModelForm):
+ class Meta:
+ model = Category
+
+ # There is some internal state in warnings module which means that
+ # if a warning has been seen already, the catch_warnings won't
+ # have recorded it. The following line therefore will not work reliably:
+
+ # self.assertEqual(w[0].category, PendingDeprecationWarning)
+
+ # Until end of the deprecation cycle, should still create the
+ # form as before:
+ self.assertEqual(list(MissingFieldsForm.base_fields),
+ ['name', 'slug', 'url'])
+
def test_extra_fields(self):
class ExtraFields(BaseCategoryForm):
some_extra_field = forms.BooleanField()
@@ -206,6 +272,33 @@ class ModelFormBaseTest(TestCase):
class Meta:
model = Category
+ fields = '__all__'
+
+ self.assertTrue(isinstance(ReplaceField.base_fields['url'],
+ forms.fields.BooleanField))
+
+ def test_replace_field_variant_2(self):
+ # Should have the same result as before,
+ # but 'fields' attribute specified differently
+ class ReplaceField(forms.ModelForm):
+ url = forms.BooleanField()
+
+ class Meta:
+ model = Category
+ fields = ['url']
+
+ self.assertTrue(isinstance(ReplaceField.base_fields['url'],
+ forms.fields.BooleanField))
+
+ def test_replace_field_variant_3(self):
+ # Should have the same result as before,
+ # but 'fields' attribute specified differently
+ class ReplaceField(forms.ModelForm):
+ url = forms.BooleanField()
+
+ class Meta:
+ model = Category
+ fields = [] # url will still appear, since it is explicit above
self.assertTrue(isinstance(ReplaceField.base_fields['url'],
forms.fields.BooleanField))
@@ -216,19 +309,11 @@ class ModelFormBaseTest(TestCase):
class Meta:
model = Author
+ fields = '__all__'
wf = AuthorForm({'name': 'Richard Lockridge'})
self.assertTrue(wf.is_valid())
- def test_limit_fields(self):
- class LimitFields(forms.ModelForm):
- class Meta:
- model = Category
- fields = ['url']
-
- self.assertEqual(list(LimitFields.base_fields),
- ['url'])
-
def test_limit_nonexistent_field(self):
expected_msg = 'Unknown field(s) (nonexistent) specified for Category'
with self.assertRaisesMessage(FieldError, expected_msg):
@@ -294,6 +379,7 @@ class ModelFormBaseTest(TestCase):
"""
class Meta:
model = Article
+ fields = '__all__'
# MixModelForm is now an Article-related thing, because MixModelForm.Meta
# overrides BaseCategoryForm.Meta.
@@ -348,6 +434,7 @@ class ModelFormBaseTest(TestCase):
class Meta:
model = Category
+ fields = '__all__'
class SubclassMeta(SomeCategoryForm):
""" We can also subclass the Meta inner class to change the fields
diff --git a/tests/model_forms_regress/tests.py b/tests/model_forms_regress/tests.py
index 90c907f2a6..0e033e033f 100644
--- a/tests/model_forms_regress/tests.py
+++ b/tests/model_forms_regress/tests.py
@@ -1,6 +1,7 @@
from __future__ import absolute_import, unicode_literals
from datetime import date
+import warnings
from django import forms
from django.core.exceptions import FieldError, ValidationError
@@ -43,9 +44,12 @@ class ModelMultipleChoiceFieldTests(TestCase):
f.clean([p.pk for p in Person.objects.all()[8:9]])
self.assertTrue(self._validator_run)
+
class TripleForm(forms.ModelForm):
class Meta:
model = Triple
+ fields = '__all__'
+
class UniqueTogetherTests(TestCase):
def test_multiple_field_unique_together(self):
@@ -63,15 +67,18 @@ class UniqueTogetherTests(TestCase):
form = TripleForm({'left': '1', 'middle': '3', 'right': '1'})
self.assertTrue(form.is_valid())
+
class TripleFormWithCleanOverride(forms.ModelForm):
class Meta:
model = Triple
+ fields = '__all__'
def clean(self):
if not self.cleaned_data['left'] == self.cleaned_data['right']:
raise forms.ValidationError('Left and right should be equal')
return self.cleaned_data
+
class OverrideCleanTests(TestCase):
def test_override_clean(self):
"""
@@ -84,6 +91,7 @@ class OverrideCleanTests(TestCase):
# by form.full_clean().
self.assertEqual(form.instance.left, 1)
+
# Regression test for #12960.
# Make sure the cleaned_data returned from ModelForm.clean() is applied to the
# model instance.
@@ -95,6 +103,8 @@ class PublicationForm(forms.ModelForm):
class Meta:
model = Publication
+ fields = '__all__'
+
class ModelFormCleanTest(TestCase):
def test_model_form_clean_applies_to_model(self):
@@ -103,9 +113,12 @@ class ModelFormCleanTest(TestCase):
publication = form.save()
self.assertEqual(publication.title, 'TEST')
+
class FPForm(forms.ModelForm):
class Meta:
model = FilePathModel
+ fields = '__all__'
+
class FilePathFieldTests(TestCase):
def test_file_path_field_blank(self):
@@ -133,7 +146,8 @@ class ManyToManyCallableInitialTests(TestCase):
book3 = Publication.objects.create(title="Third Book", date_published=date(2009,1,1))
# Create a ModelForm, instantiate it, and check that the output is as expected
- ModelForm = modelform_factory(Article, formfield_callback=formfield_for_dbfield)
+ ModelForm = modelform_factory(Article, fields="__all__",
+ formfield_callback=formfield_for_dbfield)
form = ModelForm()
self.assertHTMLEqual(form.as_ul(), """
Hold down "Control", or "Command" on a Mac, to select more than one.
"""
% (book1.pk, book2.pk, book3.pk))
+
class CFFForm(forms.ModelForm):
class Meta:
model = CustomFF
+ fields = '__all__'
+
class CustomFieldSaveTests(TestCase):
def test_save(self):
@@ -168,9 +185,12 @@ class ModelChoiceIteratorTests(TestCase):
f = Form()
self.assertEqual(len(f.fields["publications"].choices), 1)
+
class RealPersonForm(forms.ModelForm):
class Meta:
model = RealPerson
+ fields = '__all__'
+
class CustomModelFormSaveMethod(TestCase):
def test_string_message(self):
@@ -230,9 +250,12 @@ class TestTicket11183(TestCase):
self.assertTrue(field1 is not ModelChoiceForm.base_fields['person'])
self.assertTrue(field1.widget.choices.field is field1)
+
class HomepageForm(forms.ModelForm):
class Meta:
model = Homepage
+ fields = '__all__'
+
class URLFieldTests(TestCase):
def test_url_on_modelform(self):
@@ -274,6 +297,7 @@ class FormFieldCallbackTests(TestCase):
class Meta:
model = Person
widgets = {'name': widget}
+ fields = "__all__"
Form = modelform_factory(Person, form=BaseForm)
self.assertTrue(Form.base_fields['name'].widget is widget)
@@ -285,11 +309,11 @@ class FormFieldCallbackTests(TestCase):
widget = forms.Textarea()
# Without a widget should not set the widget to textarea
- Form = modelform_factory(Person)
+ Form = modelform_factory(Person, fields="__all__")
self.assertNotEqual(Form.base_fields['name'].widget.__class__, forms.Textarea)
# With a widget should not set the widget to textarea
- Form = modelform_factory(Person, widgets={'name':widget})
+ Form = modelform_factory(Person, fields="__all__", widgets={'name':widget})
self.assertEqual(Form.base_fields['name'].widget.__class__, forms.Textarea)
def test_custom_callback(self):
@@ -307,6 +331,7 @@ class FormFieldCallbackTests(TestCase):
class Meta:
model = Person
widgets = {'name': widget}
+ fields = "__all__"
_ = modelform_factory(Person, form=BaseForm,
formfield_callback=callback)
@@ -317,7 +342,7 @@ class FormFieldCallbackTests(TestCase):
def test_bad_callback(self):
# A bad callback provided by user still gives an error
- self.assertRaises(TypeError, modelform_factory, Person,
+ self.assertRaises(TypeError, modelform_factory, Person, fields="__all__",
formfield_callback='not a function or callable')
@@ -362,6 +387,8 @@ class InvalidFieldAndFactory(TestCase):
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
+ fields = '__all__'
+
class FileFieldTests(unittest.TestCase):
def test_clean_false(self):
@@ -425,6 +452,7 @@ class FileFieldTests(unittest.TestCase):
self.assertTrue('something.txt' in rendered)
self.assertTrue('myfile-clear' in rendered)
+
class EditionForm(forms.ModelForm):
author = forms.ModelChoiceField(queryset=Person.objects.all())
publication = forms.ModelChoiceField(queryset=Publication.objects.all())
@@ -433,6 +461,8 @@ class EditionForm(forms.ModelForm):
class Meta:
model = Edition
+ fields = '__all__'
+
class UniqueErrorsTests(TestCase):
def setUp(self):
@@ -473,7 +503,7 @@ class EmptyFieldsTestCase(TestCase):
def test_empty_fields_to_construct_instance(self):
"No fields should be set on a model instance if construct_instance receives fields=()"
- form = modelform_factory(Person)({'name': 'John Doe'})
+ form = modelform_factory(Person, fields="__all__")({'name': 'John Doe'})
self.assertTrue(form.is_valid())
instance = construct_instance(form, Person(), fields=())
self.assertEqual(instance.name, '')
@@ -485,10 +515,25 @@ class CustomMetaclass(ModelFormMetaclass):
new.base_fields = {}
return new
+
class CustomMetaclassForm(six.with_metaclass(CustomMetaclass, forms.ModelForm)):
pass
+
class CustomMetaclassTestCase(TestCase):
def test_modelform_factory_metaclass(self):
- new_cls = modelform_factory(Person, form=CustomMetaclassForm)
+ new_cls = modelform_factory(Person, fields="__all__", form=CustomMetaclassForm)
self.assertEqual(new_cls.base_fields, {})
+
+
+class TestTicket19733(TestCase):
+ def test_modelform_factory_without_fields(self):
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always", PendingDeprecationWarning)
+ # This should become an error once deprecation cycle is complete.
+ form = modelform_factory(Person)
+ self.assertEqual(w[0].category, PendingDeprecationWarning)
+
+ def test_modelform_factory_with_all_fields(self):
+ form = modelform_factory(Person, fields="__all__")
+ self.assertEqual(list(form.base_fields), ["name"])
diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py
index 8d0c017a61..8cfdf53995 100644
--- a/tests/model_formsets/tests.py
+++ b/tests/model_formsets/tests.py
@@ -21,7 +21,7 @@ from .models import (Author, BetterAuthor, Book, BookWithCustomPK,
class DeletionTests(TestCase):
def test_deletion(self):
- PoetFormSet = modelformset_factory(Poet, can_delete=True)
+ PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True)
poet = Poet.objects.create(name='test')
data = {
'form-TOTAL_FORMS': '1',
@@ -41,7 +41,7 @@ class DeletionTests(TestCase):
Make sure that an add form that is filled out, but marked for deletion
doesn't cause validation errors.
"""
- PoetFormSet = modelformset_factory(Poet, can_delete=True)
+ PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True)
poet = Poet.objects.create(name='test')
# One existing untouched and two new unvalid forms
data = {
@@ -75,7 +75,7 @@ class DeletionTests(TestCase):
Make sure that a change form that is filled out, but marked for deletion
doesn't cause validation errors.
"""
- PoetFormSet = modelformset_factory(Poet, can_delete=True)
+ PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True)
poet = Poet.objects.create(name='test')
data = {
'form-TOTAL_FORMS': '1',
@@ -100,7 +100,7 @@ class DeletionTests(TestCase):
class ModelFormsetTest(TestCase):
def test_simple_save(self):
qs = Author.objects.all()
- AuthorFormSet = modelformset_factory(Author, extra=3)
+ AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=3)
formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 3)
@@ -138,7 +138,7 @@ class ModelFormsetTest(TestCase):
# we'll use it to display them in alphabetical order by name.
qs = Author.objects.order_by('name')
- AuthorFormSet = modelformset_factory(Author, extra=1, can_delete=False)
+ AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=1, can_delete=False)
formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 3)
@@ -176,7 +176,7 @@ class ModelFormsetTest(TestCase):
# marked for deletion, make sure we don't save that form.
qs = Author.objects.order_by('name')
- AuthorFormSet = modelformset_factory(Author, extra=1, can_delete=True)
+ AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=1, can_delete=True)
formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 4)
@@ -256,7 +256,7 @@ class ModelFormsetTest(TestCase):
author4 = Author.objects.create(name='John Steinbeck')
- AuthorMeetingFormSet = modelformset_factory(AuthorMeeting, extra=1, can_delete=True)
+ AuthorMeetingFormSet = modelformset_factory(AuthorMeeting, fields="__all__", extra=1, can_delete=True)
data = {
'form-TOTAL_FORMS': '2', # the number of forms rendered
'form-INITIAL_FORMS': '1', # the number of forms with initial data
@@ -294,22 +294,22 @@ class ModelFormsetTest(TestCase):
qs = Author.objects.order_by('name')
- AuthorFormSet = modelformset_factory(Author, max_num=None, extra=3)
+ AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=None, extra=3)
formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 6)
self.assertEqual(len(formset.extra_forms), 3)
- AuthorFormSet = modelformset_factory(Author, max_num=4, extra=3)
+ AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=4, extra=3)
formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 4)
self.assertEqual(len(formset.extra_forms), 1)
- AuthorFormSet = modelformset_factory(Author, max_num=0, extra=3)
+ AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=0, extra=3)
formset = AuthorFormSet(queryset=qs)
self.assertEqual(len(formset.forms), 3)
self.assertEqual(len(formset.extra_forms), 0)
- AuthorFormSet = modelformset_factory(Author, max_num=None)
+ AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=None)
formset = AuthorFormSet(queryset=qs)
self.assertQuerysetEqual(formset.get_queryset(), [
'',
@@ -317,7 +317,7 @@ class ModelFormsetTest(TestCase):
'',
])
- AuthorFormSet = modelformset_factory(Author, max_num=0)
+ AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=0)
formset = AuthorFormSet(queryset=qs)
self.assertQuerysetEqual(formset.get_queryset(), [
'',
@@ -325,7 +325,7 @@ class ModelFormsetTest(TestCase):
'',
])
- AuthorFormSet = modelformset_factory(Author, max_num=4)
+ AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=4)
formset = AuthorFormSet(queryset=qs)
self.assertQuerysetEqual(formset.get_queryset(), [
'',
@@ -343,7 +343,7 @@ class ModelFormsetTest(TestCase):
author.save()
return author
- PoetFormSet = modelformset_factory(Poet, form=PoetForm)
+ PoetFormSet = modelformset_factory(Poet, fields="__all__", form=PoetForm)
data = {
'form-TOTAL_FORMS': '3', # the number of forms rendered
@@ -387,7 +387,7 @@ class ModelFormsetTest(TestCase):
self.assertFalse("subtitle" in formset.forms[0].fields)
def test_model_inheritance(self):
- BetterAuthorFormSet = modelformset_factory(BetterAuthor)
+ BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__")
formset = BetterAuthorFormSet()
self.assertEqual(len(formset.forms), 1)
self.assertHTMLEqual(formset.forms[0].as_p(),
@@ -440,7 +440,7 @@ class ModelFormsetTest(TestCase):
# We can also create a formset that is tied to a parent model. This is
# how the admin system's edit inline functionality works.
- AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=3)
+ AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=3, fields="__all__")
author = Author.objects.create(name='Charles Baudelaire')
formset = AuthorBooksFormSet(instance=author)
@@ -474,7 +474,7 @@ class ModelFormsetTest(TestCase):
# another one. This time though, an edit form will be available for
# every existing book.
- AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2)
+ AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2, fields="__all__")
author = Author.objects.get(name='Charles Baudelaire')
formset = AuthorBooksFormSet(instance=author)
@@ -514,7 +514,7 @@ class ModelFormsetTest(TestCase):
def test_inline_formsets_save_as_new(self):
# The save_as_new parameter lets you re-associate the data to a new
# instance. This is used in the admin for save_as functionality.
- AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2)
+ AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2, fields="__all__")
author = Author.objects.create(name='Charles Baudelaire')
data = {
@@ -553,7 +553,7 @@ class ModelFormsetTest(TestCase):
# primary key that is not the fk to the parent object.
self.maxDiff = 1024
- AuthorBooksFormSet2 = inlineformset_factory(Author, BookWithCustomPK, can_delete=False, extra=1)
+ AuthorBooksFormSet2 = inlineformset_factory(Author, BookWithCustomPK, can_delete=False, extra=1, fields="__all__")
author = Author.objects.create(pk=1, name='Charles Baudelaire')
formset = AuthorBooksFormSet2(instance=author)
@@ -585,7 +585,7 @@ class ModelFormsetTest(TestCase):
# Test inline formsets where the inline-edited object uses multi-table
# inheritance, thus has a non AutoField yet auto-created primary key.
- AuthorBooksFormSet3 = inlineformset_factory(Author, AlternateBook, can_delete=False, extra=1)
+ AuthorBooksFormSet3 = inlineformset_factory(Author, AlternateBook, can_delete=False, extra=1, fields="__all__")
author = Author.objects.create(pk=1, name='Charles Baudelaire')
formset = AuthorBooksFormSet3(instance=author)
@@ -616,7 +616,7 @@ class ModelFormsetTest(TestCase):
# Test inline formsets where the inline-edited object has a
# unique_together constraint with a nullable member
- AuthorBooksFormSet4 = inlineformset_factory(Author, BookWithOptionalAltEditor, can_delete=False, extra=2)
+ AuthorBooksFormSet4 = inlineformset_factory(Author, BookWithOptionalAltEditor, can_delete=False, extra=2, fields="__all__")
author = Author.objects.create(pk=1, name='Charles Baudelaire')
data = {
@@ -640,7 +640,7 @@ class ModelFormsetTest(TestCase):
self.assertEqual(book2.title, 'Les Fleurs du Mal')
def test_inline_formsets_with_custom_save_method(self):
- AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2)
+ AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2, fields="__all__")
author = Author.objects.create(pk=1, name='Charles Baudelaire')
book1 = Book.objects.create(pk=1, author=author, title='Les Paradis Artificiels')
book2 = Book.objects.create(pk=2, author=author, title='Les Fleurs du Mal')
@@ -655,7 +655,7 @@ class ModelFormsetTest(TestCase):
poem.save()
return poem
- PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm)
+ PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm, fields="__all__")
data = {
'poem_set-TOTAL_FORMS': '3', # the number of forms rendered
@@ -732,7 +732,7 @@ class ModelFormsetTest(TestCase):
def test_custom_pk(self):
# We need to ensure that it is displayed
- CustomPrimaryKeyFormSet = modelformset_factory(CustomPrimaryKey)
+ CustomPrimaryKeyFormSet = modelformset_factory(CustomPrimaryKey, fields="__all__")
formset = CustomPrimaryKeyFormSet()
self.assertEqual(len(formset.forms), 1)
self.assertHTMLEqual(formset.forms[0].as_p(),
@@ -743,7 +743,7 @@ class ModelFormsetTest(TestCase):
place = Place.objects.create(pk=1, name='Giordanos', city='Chicago')
- FormSet = inlineformset_factory(Place, Owner, extra=2, can_delete=False)
+ FormSet = inlineformset_factory(Place, Owner, extra=2, can_delete=False, fields="__all__")
formset = FormSet(instance=place)
self.assertEqual(len(formset.forms), 2)
self.assertHTMLEqual(formset.forms[0].as_p(),
@@ -799,7 +799,7 @@ class ModelFormsetTest(TestCase):
# Ensure a custom primary key that is a ForeignKey or OneToOneField get rendered for the user to choose.
- FormSet = modelformset_factory(OwnerProfile)
+ FormSet = modelformset_factory(OwnerProfile, fields="__all__")
formset = FormSet()
self.assertHTMLEqual(formset.forms[0].as_p(),
'
' % (band2.id, self.band.id))
class AdminConcertForm(forms.ModelForm):
- class Meta:
- model = Concert
+ pass
def __init__(self, *args, **kwargs):
super(AdminConcertForm, self).__init__(*args, **kwargs)
@@ -616,17 +612,6 @@ class ValidationTests(unittest.TestCase):
ValidationTestModel,
)
- class ValidationTestModelAdmin(ModelAdmin):
- fieldsets = (("General", {"fields": ("non_existent_field",)}),)
-
- six.assertRaisesRegex(self,
- ImproperlyConfigured,
- "'ValidationTestModelAdmin.fieldsets\[0\]\[1\]\['fields'\]' refers to field 'non_existent_field' that is missing from the form.",
- validate,
- ValidationTestModelAdmin,
- ValidationTestModel,
- )
-
class ValidationTestModelAdmin(ModelAdmin):
fieldsets = (("General", {"fields": ("name",)}),)
@@ -684,22 +669,6 @@ class ValidationTests(unittest.TestCase):
def test_fieldsets_with_custom_form_validation(self):
- class BandAdmin(ModelAdmin):
-
- fieldsets = (
- ('Band', {
- 'fields': ('non_existent_field',)
- }),
- )
-
- six.assertRaisesRegex(self,
- ImproperlyConfigured,
- "'BandAdmin.fieldsets\[0\]\[1\]\['fields'\]' refers to field 'non_existent_field' that is missing from the form.",
- validate,
- BandAdmin,
- Band,
- )
-
class BandAdmin(ModelAdmin):
fieldsets = (
('Band', {
@@ -709,33 +678,9 @@ class ValidationTests(unittest.TestCase):
validate(BandAdmin, Band)
- class AdminBandForm(forms.ModelForm):
- class Meta:
- model = Band
-
- class BandAdmin(ModelAdmin):
- form = AdminBandForm
-
- fieldsets = (
- ('Band', {
- 'fields': ('non_existent_field',)
- }),
- )
-
- six.assertRaisesRegex(self,
- ImproperlyConfigured,
- "'BandAdmin.fieldsets\[0]\[1\]\['fields'\]' refers to field 'non_existent_field' that is missing from the form.",
- validate,
- BandAdmin,
- Band,
- )
-
class AdminBandForm(forms.ModelForm):
delete = forms.BooleanField()
- class Meta:
- model = Band
-
class BandAdmin(ModelAdmin):
form = AdminBandForm
@@ -1371,21 +1316,6 @@ class ValidationTests(unittest.TestCase):
ValidationTestModel,
)
- class ValidationTestInline(TabularInline):
- model = ValidationTestInlineModel
- fields = ("non_existent_field",)
-
- class ValidationTestModelAdmin(ModelAdmin):
- inlines = [ValidationTestInline]
-
- six.assertRaisesRegex(self,
- ImproperlyConfigured,
- "'ValidationTestInline.fields' refers to field 'non_existent_field' that is missing from the form.",
- validate,
- ValidationTestModelAdmin,
- ValidationTestModel,
- )
-
def test_fk_name_validation(self):
class ValidationTestInline(TabularInline):
diff --git a/tests/serializers_regress/tests.py b/tests/serializers_regress/tests.py
index 72e3825d91..04b4d4c839 100644
--- a/tests/serializers_regress/tests.py
+++ b/tests/serializers_regress/tests.py
@@ -26,7 +26,7 @@ from django.test import TestCase
from django.utils import six
from django.utils.encoding import force_text
from django.utils.functional import curry
-from django.utils.unittest import skipUnless
+from django.utils.unittest import expectedFailure, skipUnless
from .models import (BinaryData, BooleanData, CharData, DateData, DateTimeData, EmailData,
FileData, FilePathData, DecimalData, FloatData, IntegerData, IPAddressData,
@@ -459,6 +459,11 @@ def serializerTest(format, self):
for klass, count in instance_count.items():
self.assertEqual(count, klass.objects.count())
+if connection.vendor == 'mysql' and six.PY3:
+ # Existing MySQL DB-API drivers fail on binary data.
+ serializerTest = expectedFailure(serializerTest)
+
+
def naturalKeySerializerTest(format, self):
# Create all the objects defined in the test data
objects = []
diff --git a/tests/staticfiles_tests/project/faulty/faulty.css b/tests/staticfiles_tests/project/faulty/faulty.css
new file mode 100644
index 0000000000..ca57c77e55
--- /dev/null
+++ b/tests/staticfiles_tests/project/faulty/faulty.css
@@ -0,0 +1 @@
+@import url("missing.css");
diff --git a/tests/staticfiles_tests/tests.py b/tests/staticfiles_tests/tests.py
index 0391b8b018..912dcffe83 100644
--- a/tests/staticfiles_tests/tests.py
+++ b/tests/staticfiles_tests/tests.py
@@ -244,7 +244,7 @@ class TestCollection(CollectionTestCase, TestDefaults):
class TestCollectionClear(CollectionTestCase):
"""
- Test the ``--clear`` option of the ``collectstatic`` managemenet command.
+ Test the ``--clear`` option of the ``collectstatic`` management command.
"""
def run_collectstatic(self, **kwargs):
clear_filepath = os.path.join(settings.STATIC_ROOT, 'cleared.txt')
@@ -550,6 +550,21 @@ class TestCollectionCachedStorage(BaseCollectionTestCase,
self.assertNotIn(b"cached/other.css", content)
self.assertIn(b"other.d41d8cd98f00.css", content)
+ @override_settings(
+ STATICFILES_DIRS=(os.path.join(TEST_ROOT, 'project', 'faulty'),),
+ STATICFILES_FINDERS=('django.contrib.staticfiles.finders.FileSystemFinder',),
+ )
+ def test_post_processing_failure(self):
+ """
+ Test that post_processing indicates the origin of the error when it
+ fails. Regression test for #18986.
+ """
+ finders._finders.clear()
+ err = six.StringIO()
+ with self.assertRaises(Exception) as cm:
+ call_command('collectstatic', interactive=False, verbosity=0, stderr=err)
+ self.assertEqual("Post-processing 'faulty.css' failed!\n\n", err.getvalue())
+
# we set DEBUG to False here since the template tag wouldn't work otherwise
@override_settings(**dict(TEST_SETTINGS,
diff --git a/tests/timezones/forms.py b/tests/timezones/forms.py
index 3c9c31167e..45fb1d080b 100644
--- a/tests/timezones/forms.py
+++ b/tests/timezones/forms.py
@@ -11,3 +11,4 @@ class EventSplitForm(forms.Form):
class EventModelForm(forms.ModelForm):
class Meta:
model = Event
+ fields = '__all__'
diff --git a/tests/utils_tests/test_datastructures.py b/tests/utils_tests/test_datastructures.py
index 3161c04f97..5829e7c2d7 100644
--- a/tests/utils_tests/test_datastructures.py
+++ b/tests/utils_tests/test_datastructures.py
@@ -203,6 +203,21 @@ class MergeDictTests(SimpleTestCase):
('key2', ['value2', 'value3']),
('key4', ['value5', 'value6'])])
+ def test_bool_casting(self):
+ empty = MergeDict({}, {}, {})
+ not_empty = MergeDict({}, {}, {"key": "value"})
+ self.assertFalse(empty)
+ self.assertTrue(not_empty)
+
+ def test_key_error(self):
+ """
+ Test that the message of KeyError contains the missing key name.
+ """
+ d1 = MergeDict({'key1': 42})
+ with six.assertRaisesRegex(self, KeyError, 'key2'):
+ d1['key2']
+
+
class MultiValueDictTests(SimpleTestCase):
def test_multivaluedict(self):
diff --git a/tests/utils_tests/test_simplelazyobject.py b/tests/utils_tests/test_simplelazyobject.py
index 883e60aa81..f925e01eb6 100644
--- a/tests/utils_tests/test_simplelazyobject.py
+++ b/tests/utils_tests/test_simplelazyobject.py
@@ -152,3 +152,12 @@ class TestUtilsSimpleLazyObject(TestCase):
SimpleLazyObject(None)
finally:
sys.settrace(old_trace_func)
+
+ def test_not_equal(self):
+ lazy1 = SimpleLazyObject(lambda: 2)
+ lazy2 = SimpleLazyObject(lambda: 2)
+ lazy3 = SimpleLazyObject(lambda: 3)
+ self.assertEqual(lazy1, lazy2)
+ self.assertNotEqual(lazy1, lazy3)
+ self.assertTrue(lazy1 != lazy3)
+ self.assertFalse(lazy1 != lazy2)
diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py
index 5a3bdd1062..2da17dd0f5 100644
--- a/tests/view_tests/tests/test_i18n.py
+++ b/tests/view_tests/tests/test_i18n.py
@@ -217,5 +217,6 @@ class JavascriptI18nTests(LiveServerTestCase):
def test_escaping(self):
extended_apps = list(settings.INSTALLED_APPS) + ['view_tests']
with self.settings(INSTALLED_APPS=extended_apps):
- response = self.client.get('%s%s' % (self.live_server_url, '/jsi18n_admin/'))
+ # Force a language via GET otherwise the gettext functions are a noop!
+ response = self.client.get('/jsi18n_admin/?language=de')
self.assertContains(response, '\\x04')