Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge remote-tracking branch 'core/master' into schema-alteration

Conflicts:
	django/db/backends/mysql/base.py
	django/db/backends/postgresql_psycopg2/base.py
  • Loading branch information...
commit 9313dea7006ab77d54735fdd5825a812684e7144 2 parents dbf8b93 + d21f3d9
Andrew Godwin andrewgodwin authored
Showing with 2,045 additions and 1,256 deletions.
  1. +3 −1 AUTHORS
  2. +13 −5 django/conf/__init__.py
  3. +6 −3 django/contrib/admin/filters.py
  4. +4 −1 django/contrib/admin/options.py
  5. +254 −118 django/contrib/admin/static/admin/js/inlines.js
  6. +9 −5 django/contrib/admin/static/admin/js/inlines.min.js
  7. +6 −58 django/contrib/admin/templates/admin/edit_inline/stacked.html
  8. +7 −58 django/contrib/admin/templates/admin/edit_inline/tabular.html
  9. +1 −1  django/contrib/admin/templatetags/admin_list.py
  10. +1 −1  django/contrib/admin/util.py
  11. +7 −4 django/contrib/auth/decorators.py
  12. +14 −14 django/contrib/auth/forms.py
  13. +7 −1 django/contrib/auth/hashers.py
  14. +1 −1  django/contrib/auth/models.py
  15. +1 −1  django/contrib/auth/tests/decorators.py
  16. +12 −6 django/contrib/auth/tests/forms.py
  17. +4 −0 django/contrib/auth/tests/hashers.py
  18. +3 −2 django/contrib/auth/tests/views.py
  19. +9 −9 django/contrib/auth/views.py
  20. +0 −2  django/contrib/gis/db/backends/base.py
  21. +0 −14 django/contrib/gis/db/backends/util.py
  22. +1 −1  django/contrib/gis/db/models/sql/where.py
  23. +5 −1 django/contrib/gis/gdal/tests/test_ds.py
  24. +4 −2 django/contrib/gis/gdal/tests/test_geom.py
  25. +1 −1  django/contrib/gis/geos/libgeos.py
  26. +6 −3 django/contrib/gis/geos/mutable_list.py
  27. +2 −1  django/contrib/gis/geos/tests/__init__.py
  28. +4 −2 django/contrib/gis/geos/tests/test_geos.py
  29. +8 −8 django/contrib/gis/geos/tests/test_mutable_list.py
  30. +2 −1  django/contrib/gis/tests/geoapp/tests.py
  31. +12 −20 django/contrib/gis/tests/test_spatialrefsys.py
  32. +6 −0 django/contrib/markup/templatetags/markup.py
  33. +10 −1 django/contrib/markup/tests.py
  34. +4 −4 django/contrib/messages/storage/cookie.py
  35. +5 −5 django/contrib/messages/tests/base.py
  36. +7 −7 django/contrib/sessions/tests.py
  37. +11 −12 django/core/handlers/wsgi.py
  38. +2 −1  django/core/management/__init__.py
  39. +3 −0  django/core/management/commands/runserver.py
  40. +5 −2 django/db/backends/__init__.py
  41. +2 −1  django/db/backends/mysql/base.py
  42. +0 −10 django/db/backends/mysql/introspection.py
  43. +2 −1  django/db/backends/postgresql_psycopg2/base.py
  44. +7 −0 django/db/models/constants.py
  45. +1 −2  django/db/models/query.py
  46. +7 −2 django/db/models/sql/compiler.py
  47. +7 −4 django/db/models/sql/constants.py
  48. +1 −1  django/db/models/sql/expressions.py
  49. +3 −2 django/db/models/sql/query.py
  50. +1 −0  django/db/models/sql/subqueries.py
  51. +1 −1  django/forms/fields.py
  52. +11 −7 django/forms/widgets.py
  53. +35 −21 django/http/__init__.py
  54. +1 −1  django/middleware/csrf.py
  55. +32 −17 django/shortcuts/__init__.py
  56. +2 −4 django/template/defaulttags.py
  57. +7 −0 django/test/signals.py
  58. +10 −7 django/utils/_os.py
  59. +1 −2  django/utils/html_parser.py
  60. +3 −0  django/views/generic/dates.py
  61. +2 −3 docs/README
  62. +0 −11 docs/contents.txt
  63. +2 −3 docs/faq/admin.txt
  64. +17 −23 docs/faq/install.txt
  65. +1 −1  docs/faq/models.txt
  66. +4 −4 docs/howto/custom-model-fields.txt
  67. +1 −1  docs/index.txt
  68. +28 −10 docs/internals/committers.txt
  69. +3 −0  docs/internals/deprecation.txt
  70. +3 −5 docs/intro/install.txt
  71. +4 −4 docs/intro/overview.txt
  72. +3 −2 docs/intro/tutorial03.txt
  73. +2 −2 docs/intro/tutorial04.txt
  74. +1 −2  docs/intro/whatsnext.txt
  75. BIN  docs/obsolete/_images/formrow.png
  76. BIN  docs/obsolete/_images/module.png
  77. BIN  docs/obsolete/_images/objecttools_01.png
  78. BIN  docs/obsolete/_images/objecttools_02.png
  79. +0 −186 docs/obsolete/admin-css.txt
  80. +0 −12 docs/obsolete/index.txt
  81. +35 −30 docs/ref/class-based-views/base.txt
  82. +79 −49 docs/ref/class-based-views/generic-date-based.txt
  83. +56 −1 docs/ref/class-based-views/generic-display.txt
  84. +5 −4 docs/ref/class-based-views/index.txt
  85. +114 −59 docs/ref/class-based-views/mixins-date-based.txt
  86. +2 −1  docs/ref/class-based-views/mixins-multiple-object.txt
  87. +17 −8 docs/ref/class-based-views/mixins-simple.txt
  88. +4 −4 docs/ref/contrib/comments/moderation.txt
  89. +8 −0 docs/ref/contrib/contenttypes.txt
  90. +61 −1 docs/ref/contrib/formtools/form-wizard.txt
  91. +6 −6 docs/ref/contrib/gis/install.txt
  92. +2 −2 docs/ref/contrib/gis/tutorial.txt
  93. +3 −0  docs/ref/contrib/markup.txt
  94. +47 −49 docs/ref/contrib/messages.txt
  95. +44 −17 docs/ref/forms/fields.txt
  96. +178 −58 docs/ref/forms/widgets.txt
  97. +10 −2 docs/ref/models/fields.txt
  98. +1 −1  docs/ref/models/instances.txt
  99. +51 −6 docs/ref/models/querysets.txt
  100. +13 −23 docs/ref/settings.txt
  101. +14 −0 docs/releases/1.5.txt
  102. +1 −0  docs/releases/index.txt
  103. +37 −0 docs/topics/auth.txt
  104. +44 −0 docs/topics/class-based-views/index.txt
  105. +4 −4 docs/topics/db/examples/many_to_one.txt
  106. +92 −36 docs/topics/db/multi-db.txt
  107. +12 −11 docs/topics/db/queries.txt
  108. +12 −12 docs/topics/forms/media.txt
  109. +3 −3 docs/topics/forms/modelforms.txt
  110. +52 −50 docs/topics/http/urls.txt
  111. +5 −0 docs/topics/i18n/translation.txt
  112. +5 −8 docs/topics/install.txt
  113. +6 −6 docs/topics/logging.txt
  114. +9 −2 docs/topics/python3.txt
  115. +59 −2 tests/regressiontests/admin_inlines/tests.py
  116. +19 −19 tests/regressiontests/admin_scripts/tests.py
  117. +15 −3 tests/regressiontests/admin_views/admin.py
  118. +1 −0  tests/regressiontests/admin_views/customadmin.py
  119. +7 −0 tests/regressiontests/admin_views/models.py
  120. +19 −1 tests/regressiontests/admin_views/tests.py
  121. +13 −0 tests/regressiontests/backends/tests.py
  122. +1 −1  tests/regressiontests/comment_tests/tests/__init__.py
  123. +9 −0 tests/regressiontests/comment_tests/urls_default.py
  124. +3 −0  tests/regressiontests/defer_regress/models.py
  125. +15 −1 tests/regressiontests/defer_regress/tests.py
  126. +9 −9 tests/regressiontests/forms/tests/widgets.py
  127. +42 −22 tests/regressiontests/httpwrappers/tests.py
  128. +13 −0 tests/regressiontests/model_permalink/models.py
  129. +9 −0 tests/regressiontests/model_permalink/tests.py
  130. 0  tests/regressiontests/resolve_url/__init__.py
  131. +12 −0 tests/regressiontests/resolve_url/models.py
  132. +68 −0 tests/regressiontests/resolve_url/tests.py
  133. +26 −0 tests/regressiontests/utils/os_utils.py
  134. +1 −0  tests/regressiontests/utils/tests.py
  135. +6 −15 tests/regressiontests/views/tests/debug.py
  136. +1 −1  tests/runtests.py
4 AUTHORS
View
@@ -31,6 +31,8 @@ The PRIMARY AUTHORS are (and/or have been):
* Claude Paroz
* Anssi Kääriäinen
* Florian Apolloner
+ * Jeremy Dunck
+ * Bryan Veloso
More information on the main contributors to Django can be found in
docs/internals/committers.txt.
@@ -167,7 +169,6 @@ answer newbie questions, and generally made Django that much better:
dready <wil@mojipage.com>
Maximillian Dornseif <md@hudora.de>
Daniel Duan <DaNmarner@gmail.com>
- Jeremy Dunck <http://dunck.us/>
Andrew Durdin <adurdin@gmail.com>
dusk@woofle.net
Andy Dustman <farcepest@gmail.com>
@@ -506,6 +507,7 @@ answer newbie questions, and generally made Django that much better:
Johan C. Stöver <johan@nilling.nl>
Nowell Strite <http://nowell.strite.org/>
Thomas Stromberg <tstromberg@google.com>
+ Travis Swicegood <travis@domain51.com>
Pascal Varet
SuperJared
Radek Švarz <http://www.svarz.cz/translate/>
18 django/conf/__init__.py
View
@@ -7,7 +7,6 @@
"""
import os
-import re
import time # Needed for Windows
import warnings
@@ -26,7 +25,7 @@ class LazySettings(LazyObject):
The user can manually configure settings prior to using them. Otherwise,
Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE.
"""
- def _setup(self):
+ def _setup(self, name):
"""
Load the settings module pointed to by the environment variable. This
is used the first time we need any settings at all, if the user has not
@@ -37,12 +36,21 @@ def _setup(self):
if not settings_module: # If it's set but is an empty string.
raise KeyError
except KeyError:
- # NOTE: This is arguably an EnvironmentError, but that causes
- # problems with Python's interactive help.
- raise ImportError("Settings cannot be imported, because environment variable %s is undefined." % ENVIRONMENT_VARIABLE)
+ raise ImproperlyConfigured(
+ "Requested setting %s, but settings are not configured. "
+ "You must either define the environment variable %s "
+ "or call settings.configure() before accessing settings."
+ % (name, ENVIRONMENT_VARIABLE))
self._wrapped = Settings(settings_module)
+
+ def __getattr__(self, name):
+ if self._wrapped is empty:
+ self._setup(name)
+ return getattr(self._wrapped, name)
+
+
def configure(self, default_settings=global_settings, **options):
"""
Called to manually configure the settings. The 'default_settings'
9 django/contrib/admin/filters.py
View
@@ -8,13 +8,13 @@
import datetime
from django.db import models
-from django.core.exceptions import ImproperlyConfigured
+from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.utils.encoding import smart_text
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
-
from django.contrib.admin.util import (get_model_from_relation,
reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
+from django.contrib.admin.options import IncorrectLookupParameters
class ListFilter(object):
title = None # Human-readable title to appear in the right sidebar.
@@ -129,7 +129,10 @@ def has_output(self):
return True
def queryset(self, request, queryset):
- return queryset.filter(**self.used_parameters)
+ try:
+ return queryset.filter(**self.used_parameters)
+ except ValidationError as e:
+ raise IncorrectLookupParameters(e)
@classmethod
def register(cls, test, list_filter_class, take_priority=False):
5 django/contrib/admin/options.py
View
@@ -14,9 +14,10 @@
from django.core.paginator import Paginator
from django.core.urlresolvers import reverse
from django.db import models, transaction, router
+from django.db.models.constants import LOOKUP_SEP
from django.db.models.related import RelatedObject
from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
-from django.db.models.sql.constants import LOOKUP_SEP, QUERY_TERMS
+from django.db.models.sql.constants import QUERY_TERMS
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template.response import SimpleTemplateResponse, TemplateResponse
@@ -1456,8 +1457,10 @@ def has_delete_permission(self, request, obj=None):
return request.user.has_perm(
self.opts.app_label + '.' + self.opts.get_delete_permission())
+
class StackedInline(InlineModelAdmin):
template = 'admin/edit_inline/stacked.html'
+
class TabularInline(InlineModelAdmin):
template = 'admin/edit_inline/tabular.html'
372 django/contrib/admin/static/admin/js/inlines.js
View
@@ -9,128 +9,264 @@
* All rights reserved.
*
* Spiced up with Code from Zain Memon's GSoC project 2009
- * and modified for Django by Jannis Leidel
+ * and modified for Django by Jannis Leidel, Travis Swicegood and Julien Phalip.
*
* Licensed under the New BSD License
* See: http://www.opensource.org/licenses/bsd-license.php
*/
(function($) {
- $.fn.formset = function(opts) {
- var options = $.extend({}, $.fn.formset.defaults, opts);
- var updateElementIndex = function(el, prefix, ndx) {
- var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
- var replacement = prefix + "-" + ndx;
- if ($(el).attr("for")) {
- $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
- }
- if (el.id) {
- el.id = el.id.replace(id_regex, replacement);
- }
- if (el.name) {
- el.name = el.name.replace(id_regex, replacement);
- }
- };
- var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
- var nextIndex = parseInt(totalForms.val(), 10);
- var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off");
- // only show the add button if we are allowed to add more items,
+ $.fn.formset = function(opts) {
+ var options = $.extend({}, $.fn.formset.defaults, opts);
+ var $this = $(this);
+ var $parent = $this.parent();
+ var updateElementIndex = function(el, prefix, ndx) {
+ var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
+ var replacement = prefix + "-" + ndx;
+ if ($(el).attr("for")) {
+ $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
+ }
+ if (el.id) {
+ el.id = el.id.replace(id_regex, replacement);
+ }
+ if (el.name) {
+ el.name = el.name.replace(id_regex, replacement);
+ }
+ };
+ var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
+ var nextIndex = parseInt(totalForms.val(), 10);
+ var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off");
+ // only show the add button if we are allowed to add more items,
// note that max_num = None translates to a blank string.
- var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
- $(this).each(function(i) {
- $(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
- });
- if ($(this).length && showAddButton) {
- var addButton;
- if ($(this).attr("tagName") == "TR") {
- // If forms are laid out as table rows, insert the
- // "add" button in a new table row:
- var numCols = this.eq(-1).children().length;
- $(this).parent().append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
- addButton = $(this).parent().find("tr:last a");
- } else {
- // Otherwise, insert it immediately after the last form:
- $(this).filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
- addButton = $(this).filter(":last").next().find("a");
- }
- addButton.click(function(e) {
- e.preventDefault();
- var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
- var template = $("#" + options.prefix + "-empty");
- var row = template.clone(true);
- row.removeClass(options.emptyCssClass)
- .addClass(options.formCssClass)
- .attr("id", options.prefix + "-" + nextIndex);
- if (row.is("tr")) {
- // If the forms are laid out in table rows, insert
- // the remove button into the last table cell:
- row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
- } else if (row.is("ul") || row.is("ol")) {
- // If they're laid out as an ordered/unordered list,
- // insert an <li> after the last list item:
- row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
- } else {
- // Otherwise, just insert the remove button as the
- // last child element of the form's container:
- row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
- }
- row.find("*").each(function() {
- updateElementIndex(this, options.prefix, totalForms.val());
- });
- // Insert the new form when it has been fully edited
- row.insertBefore($(template));
- // Update number of total forms
- $(totalForms).val(parseInt(totalForms.val(), 10) + 1);
- nextIndex += 1;
- // Hide add button in case we've hit the max, except we want to add infinitely
- if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
- addButton.parent().hide();
- }
- // The delete button of each row triggers a bunch of other things
- row.find("a." + options.deleteCssClass).click(function(e) {
- e.preventDefault();
- // Remove the parent form containing this button:
- var row = $(this).parents("." + options.formCssClass);
- row.remove();
- nextIndex -= 1;
- // If a post-delete callback was provided, call it with the deleted form:
- if (options.removed) {
- options.removed(row);
- }
- // Update the TOTAL_FORMS form count.
- var forms = $("." + options.formCssClass);
- $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
- // Show add button again once we drop below max
- if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
- addButton.parent().show();
- }
- // Also, update names and ids for all remaining form controls
- // so they remain in sequence:
- for (var i=0, formCount=forms.length; i<formCount; i++)
- {
- updateElementIndex($(forms).get(i), options.prefix, i);
- $(forms.get(i)).find("*").each(function() {
- updateElementIndex(this, options.prefix, i);
- });
- }
- });
- // If a post-add callback was supplied, call it with the added form:
- if (options.added) {
- options.added(row);
- }
- });
- }
- return this;
- };
- /* Setup plugin defaults */
- $.fn.formset.defaults = {
- prefix: "form", // The form prefix for your django formset
- addText: "add another", // Text for the add link
- deleteText: "remove", // Text for the delete link
- addCssClass: "add-row", // CSS class applied to the add link
- deleteCssClass: "delete-row", // CSS class applied to the delete link
- emptyCssClass: "empty-row", // CSS class applied to the empty row
- formCssClass: "dynamic-form", // CSS class applied to each form in a formset
- added: null, // Function called each time a new form is added
- removed: null // Function called each time a form is deleted
- };
+ var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
+ $this.each(function(i) {
+ $(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
+ });
+ if ($this.length && showAddButton) {
+ var addButton;
+ if ($this.attr("tagName") == "TR") {
+ // If forms are laid out as table rows, insert the
+ // "add" button in a new table row:
+ var numCols = this.eq(-1).children().length;
+ $parent.append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
+ addButton = $parent.find("tr:last a");
+ } else {
+ // Otherwise, insert it immediately after the last form:
+ $this.filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
+ addButton = $this.filter(":last").next().find("a");
+ }
+ addButton.click(function(e) {
+ e.preventDefault();
+ var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
+ var template = $("#" + options.prefix + "-empty");
+ var row = template.clone(true);
+ row.removeClass(options.emptyCssClass)
+ .addClass(options.formCssClass)
+ .attr("id", options.prefix + "-" + nextIndex);
+ if (row.is("tr")) {
+ // If the forms are laid out in table rows, insert
+ // the remove button into the last table cell:
+ row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
+ } else if (row.is("ul") || row.is("ol")) {
+ // If they're laid out as an ordered/unordered list,
+ // insert an <li> after the last list item:
+ row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
+ } else {
+ // Otherwise, just insert the remove button as the
+ // last child element of the form's container:
+ row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
+ }
+ row.find("*").each(function() {
+ updateElementIndex(this, options.prefix, totalForms.val());
+ });
+ // Insert the new form when it has been fully edited
+ row.insertBefore($(template));
+ // Update number of total forms
+ $(totalForms).val(parseInt(totalForms.val(), 10) + 1);
+ nextIndex += 1;
+ // Hide add button in case we've hit the max, except we want to add infinitely
+ if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
+ addButton.parent().hide();
+ }
+ // The delete button of each row triggers a bunch of other things
+ row.find("a." + options.deleteCssClass).click(function(e) {
+ e.preventDefault();
+ // Remove the parent form containing this button:
+ var row = $(this).parents("." + options.formCssClass);
+ row.remove();
+ nextIndex -= 1;
+ // If a post-delete callback was provided, call it with the deleted form:
+ if (options.removed) {
+ options.removed(row);
+ }
+ // Update the TOTAL_FORMS form count.
+ var forms = $("." + options.formCssClass);
+ $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
+ // Show add button again once we drop below max
+ if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
+ addButton.parent().show();
+ }
+ // Also, update names and ids for all remaining form controls
+ // so they remain in sequence:
+ for (var i=0, formCount=forms.length; i<formCount; i++)
+ {
+ updateElementIndex($(forms).get(i), options.prefix, i);
+ $(forms.get(i)).find("*").each(function() {
+ updateElementIndex(this, options.prefix, i);
+ });
+ }
+ });
+ // If a post-add callback was supplied, call it with the added form:
+ if (options.added) {
+ options.added(row);
+ }
+ });
+ }
+ return this;
+ };
+
+ /* Setup plugin defaults */
+ $.fn.formset.defaults = {
+ prefix: "form", // The form prefix for your django formset
+ addText: "add another", // Text for the add link
+ deleteText: "remove", // Text for the delete link
+ addCssClass: "add-row", // CSS class applied to the add link
+ deleteCssClass: "delete-row", // CSS class applied to the delete link
+ emptyCssClass: "empty-row", // CSS class applied to the empty row
+ formCssClass: "dynamic-form", // CSS class applied to each form in a formset
+ added: null, // Function called each time a new form is added
+ removed: null // Function called each time a form is deleted
+ };
+
+
+ // Tabular inlines ---------------------------------------------------------
+ $.fn.tabularFormset = function(options) {
+ var $rows = $(this);
+ var alternatingRows = function(row) {
+ $($rows.selector).not(".add-row").removeClass("row1 row2")
+ .filter(":even").addClass("row1").end()
+ .filter(":odd").addClass("row2");
+ };
+
+ var reinitDateTimeShortCuts = function() {
+ // Reinitialize the calendar and clock widgets by force
+ if (typeof DateTimeShortcuts != "undefined") {
+ $(".datetimeshortcuts").remove();
+ DateTimeShortcuts.init();
+ }
+ };
+
+ var updateSelectFilter = function() {
+ // If any SelectFilter widgets are a part of the new form,
+ // instantiate a new SelectFilter instance for it.
+ if (typeof SelectFilter != 'undefined'){
+ $('.selectfilter').each(function(index, value){
+ var namearr = value.name.split('-');
+ SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix );
+ });
+ $('.selectfilterstacked').each(function(index, value){
+ var namearr = value.name.split('-');
+ SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix );
+ });
+ }
+ };
+
+ var initPrepopulatedFields = function(row) {
+ row.find('.prepopulated_field').each(function() {
+ var field = $(this),
+ input = field.find('input, select, textarea'),
+ dependency_list = input.data('dependency_list') || [],
+ dependencies = [];
+ $.each(dependency_list, function(i, field_name) {
+ dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
+ });
+ if (dependencies.length) {
+ input.prepopulate(dependencies, input.attr('maxlength'));
+ }
+ });
+ };
+
+ $rows.formset({
+ prefix: options.prefix,
+ addText: options.addText,
+ formCssClass: "dynamic-" + options.prefix,
+ deleteCssClass: "inline-deletelink",
+ deleteText: options.deleteText,
+ emptyCssClass: "empty-form",
+ removed: alternatingRows,
+ added: function(row) {
+ initPrepopulatedFields(row);
+ reinitDateTimeShortCuts();
+ updateSelectFilter();
+ alternatingRows(row);
+ }
+ });
+
+ return $rows;
+ };
+
+ // Stacked inlines ---------------------------------------------------------
+ $.fn.stackedFormset = function(options) {
+ var $rows = $(this);
+ var updateInlineLabel = function(row) {
+ $($rows.selector).find(".inline_label").each(function(i) {
+ var count = i + 1;
+ $(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
+ });
+ };
+
+ var reinitDateTimeShortCuts = function() {
+ // Reinitialize the calendar and clock widgets by force, yuck.
+ if (typeof DateTimeShortcuts != "undefined") {
+ $(".datetimeshortcuts").remove();
+ DateTimeShortcuts.init();
+ }
+ };
+
+ var updateSelectFilter = function() {
+ // If any SelectFilter widgets were added, instantiate a new instance.
+ if (typeof SelectFilter != "undefined"){
+ $(".selectfilter").each(function(index, value){
+ var namearr = value.name.split('-');
+ SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix);
+ });
+ $(".selectfilterstacked").each(function(index, value){
+ var namearr = value.name.split('-');
+ SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix);
+ });
+ }
+ };
+
+ var initPrepopulatedFields = function(row) {
+ row.find('.prepopulated_field').each(function() {
+ var field = $(this),
+ input = field.find('input, select, textarea'),
+ dependency_list = input.data('dependency_list') || [],
+ dependencies = [];
+ $.each(dependency_list, function(i, field_name) {
+ dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
+ });
+ if (dependencies.length) {
+ input.prepopulate(dependencies, input.attr('maxlength'));
+ }
+ });
+ };
+
+ $rows.formset({
+ prefix: options.prefix,
+ addText: options.addText,
+ formCssClass: "dynamic-" + options.prefix,
+ deleteCssClass: "inline-deletelink",
+ deleteText: options.deleteText,
+ emptyCssClass: "empty-form",
+ removed: updateInlineLabel,
+ added: (function(row) {
+ initPrepopulatedFields(row);
+ reinitDateTimeShortCuts();
+ updateSelectFilter();
+ updateInlineLabel(row);
+ })
+ });
+
+ return $rows;
+ };
})(django.jQuery);
14 django/contrib/admin/static/admin/js/inlines.min.js
View
@@ -1,5 +1,9 @@
-(function(b){b.fn.formset=function(g){var a=b.extend({},b.fn.formset.defaults,g),k=function(c,f,e){var d=RegExp("("+f+"-(\\d+|__prefix__))");f=f+"-"+e;b(c).attr("for")&&b(c).attr("for",b(c).attr("for").replace(d,f));if(c.id)c.id=c.id.replace(d,f);if(c.name)c.name=c.name.replace(d,f)};g=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off");var l=parseInt(g.val(),10),h=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off");g=h.val()===""||h.val()-g.val()>0;b(this).each(function(){b(this).not("."+
-a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&g){var j;if(b(this).attr("tagName")=="TR"){g=this.eq(-1).children().length;b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+g+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>");j=b(this).parent().find("tr:last a")}else{b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>");j=b(this).filter(":last").next().find("a")}j.click(function(c){c.preventDefault();
-var f=b("#id_"+a.prefix+"-TOTAL_FORMS");c=b("#"+a.prefix+"-empty");var e=c.clone(true);e.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);if(e.is("tr"))e.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>");else e.is("ul")||e.is("ol")?e.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):e.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+
-a.deleteText+"</a></span>");e.find("*").each(function(){k(this,a.prefix,f.val())});e.insertBefore(b(c));b(f).val(parseInt(f.val(),10)+1);l+=1;h.val()!==""&&h.val()-f.val()<=0&&j.parent().hide();e.find("a."+a.deleteCssClass).click(function(d){d.preventDefault();d=b(this).parents("."+a.formCssClass);d.remove();l-=1;a.removed&&a.removed(d);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);if(h.val()===""||h.val()-d.length>0)j.parent().show();for(var i=0,m=d.length;i<m;i++){k(b(d).get(i),
-a.prefix,i);b(d.get(i)).find("*").each(function(){k(this,a.prefix,i)})}});a.added&&a.added(e)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery);
+(function(b){b.fn.formset=function(d){var a=b.extend({},b.fn.formset.defaults,d),c=b(this),d=c.parent(),i=function(a,e,g){var d=RegExp("("+e+"-(\\d+|__prefix__))"),e=e+"-"+g;b(a).attr("for")&&b(a).attr("for",b(a).attr("for").replace(d,e));a.id&&(a.id=a.id.replace(d,e));a.name&&(a.name=a.name.replace(d,e))},f=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off"),g=parseInt(f.val(),10),e=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off"),f=""===e.val()||0<e.val()-f.val();c.each(function(){b(this).not("."+
+a.emptyCssClass).addClass(a.formCssClass)});if(c.length&&f){var h;"TR"==c.attr("tagName")?(c=this.eq(-1).children().length,d.append('<tr class="'+a.addCssClass+'"><td colspan="'+c+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>"),h=d.find("tr:last a")):(c.filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>"),h=c.filter(":last").next().find("a"));h.click(function(d){d.preventDefault();var f=b("#id_"+a.prefix+"-TOTAL_FORMS"),d=b("#"+a.prefix+
+"-empty"),c=d.clone(true);c.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+g);c.is("tr")?c.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>"):c.is("ul")||c.is("ol")?c.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):c.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></span>");c.find("*").each(function(){i(this,
+a.prefix,f.val())});c.insertBefore(b(d));b(f).val(parseInt(f.val(),10)+1);g=g+1;e.val()!==""&&e.val()-f.val()<=0&&h.parent().hide();c.find("a."+a.deleteCssClass).click(function(d){d.preventDefault();d=b(this).parents("."+a.formCssClass);d.remove();g=g-1;a.removed&&a.removed(d);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);(e.val()===""||e.val()-d.length>0)&&h.parent().show();for(var c=0,f=d.length;c<f;c++){i(b(d).get(c),a.prefix,c);b(d.get(c)).find("*").each(function(){i(this,
+a.prefix,c)})}});a.added&&a.added(c)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null};b.fn.tabularFormset=function(d){var a=b(this),c=function(){b(a.selector).not(".add-row").removeClass("row1 row2").filter(":even").addClass("row1").end().filter(":odd").addClass("row2")};a.formset({prefix:d.prefix,addText:d.addText,formCssClass:"dynamic-"+
+d.prefix,deleteCssClass:"inline-deletelink",deleteText:d.deleteText,emptyCssClass:"empty-form",removed:c,added:function(a){a.find(".prepopulated_field").each(function(){var d=b(this).find("input, select, textarea"),c=d.data("dependency_list")||[],e=[];b.each(c,function(d,b){e.push("#"+a.find(".field-"+b).find("input, select, textarea").attr("id"))});e.length&&d.prepopulate(e,d.attr("maxlength"))});"undefined"!=typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),DateTimeShortcuts.init());"undefined"!=
+typeof SelectFilter&&(b(".selectfilter").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],false,d.adminStaticPrefix)}),b(".selectfilterstacked").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],true,d.adminStaticPrefix)}));c(a)}});return a};b.fn.stackedFormset=function(d){var a=b(this),c=function(){b(a.selector).find(".inline_label").each(function(a){a+=1;b(this).html(b(this).html().replace(/(#\d+)/g,"#"+a))})};a.formset({prefix:d.prefix,
+addText:d.addText,formCssClass:"dynamic-"+d.prefix,deleteCssClass:"inline-deletelink",deleteText:d.deleteText,emptyCssClass:"empty-form",removed:c,added:function(a){a.find(".prepopulated_field").each(function(){var d=b(this).find("input, select, textarea"),c=d.data("dependency_list")||[],e=[];b.each(c,function(d,b){e.push("#"+a.find(".form-row .field-"+b).find("input, select, textarea").attr("id"))});e.length&&d.prepopulate(e,d.attr("maxlength"))});"undefined"!=typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),
+DateTimeShortcuts.init());"undefined"!=typeof SelectFilter&&(b(".selectfilter").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],false,d.adminStaticPrefix)}),b(".selectfilterstacked").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],true,d.adminStaticPrefix)}));c(a)}});return a}})(django.jQuery);
64 django/contrib/admin/templates/admin/edit_inline/stacked.html
View
@@ -20,63 +20,11 @@
<script type="text/javascript">
(function($) {
- $(document).ready(function() {
- var rows = "#{{ inline_admin_formset.formset.prefix }}-group .inline-related";
- var updateInlineLabel = function(row) {
- $(rows).find(".inline_label").each(function(i) {
- var count = i + 1;
- $(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
- });
- };
- var reinitDateTimeShortCuts = function() {
- // Reinitialize the calendar and clock widgets by force, yuck.
- if (typeof DateTimeShortcuts != "undefined") {
- $(".datetimeshortcuts").remove();
- DateTimeShortcuts.init();
- }
- };
- var updateSelectFilter = function() {
- // If any SelectFilter widgets were added, instantiate a new instance.
- if (typeof SelectFilter != "undefined"){
- $(".selectfilter").each(function(index, value){
- var namearr = value.name.split('-');
- SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static "admin/" %}");
- });
- $(".selectfilterstacked").each(function(index, value){
- var namearr = value.name.split('-');
- SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static "admin/" %}");
- });
- }
- };
- var initPrepopulatedFields = function(row) {
- row.find('.prepopulated_field').each(function() {
- var field = $(this);
- var input = field.find('input, select, textarea');
- var dependency_list = input.data('dependency_list') || [];
- var dependencies = [];
- $.each(dependency_list, function(i, field_name) {
- dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
- });
- if (dependencies.length) {
- input.prepopulate(dependencies, input.attr('maxlength'));
- }
- });
- };
- $(rows).formset({
- prefix: "{{ inline_admin_formset.formset.prefix }}",
- addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}",
- formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
- deleteCssClass: "inline-deletelink",
- deleteText: "{% trans "Remove" %}",
- emptyCssClass: "empty-form",
- removed: updateInlineLabel,
- added: (function(row) {
- initPrepopulatedFields(row);
- reinitDateTimeShortCuts();
- updateSelectFilter();
- updateInlineLabel(row);
- })
- });
- });
+ $("#{{ inline_admin_formset.formset.prefix }}-group .inline-related").stackedFormset({
+ prefix: '{{ inline_admin_formset.formset.prefix }}',
+ adminStaticPrefix: '{% static "admin/" %}',
+ deleteText: "{% trans "Remove" %}",
+ addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}"
+ });
})(django.jQuery);
</script>
65 django/contrib/admin/templates/admin/edit_inline/tabular.html
View
@@ -67,64 +67,13 @@
</div>
<script type="text/javascript">
+
(function($) {
- $(document).ready(function($) {
- var rows = "#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr";
- var alternatingRows = function(row) {
- $(rows).not(".add-row").removeClass("row1 row2")
- .filter(":even").addClass("row1").end()
- .filter(rows + ":odd").addClass("row2");
- }
- var reinitDateTimeShortCuts = function() {
- // Reinitialize the calendar and clock widgets by force
- if (typeof DateTimeShortcuts != "undefined") {
- $(".datetimeshortcuts").remove();
- DateTimeShortcuts.init();
- }
- }
- var updateSelectFilter = function() {
- // If any SelectFilter widgets are a part of the new form,
- // instantiate a new SelectFilter instance for it.
- if (typeof SelectFilter != "undefined"){
- $(".selectfilter").each(function(index, value){
- var namearr = value.name.split('-');
- SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static "admin/" %}");
- });
- $(".selectfilterstacked").each(function(index, value){
- var namearr = value.name.split('-');
- SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static "admin/" %}");
- });
- }
- }
- var initPrepopulatedFields = function(row) {
- row.find('.prepopulated_field').each(function() {
- var field = $(this);
- var input = field.find('input, select, textarea');
- var dependency_list = input.data('dependency_list') || [];
- var dependencies = [];
- $.each(dependency_list, function(i, field_name) {
- dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
- });
- if (dependencies.length) {
- input.prepopulate(dependencies, input.attr('maxlength'));
- }
- });
- }
- $(rows).formset({
- prefix: "{{ inline_admin_formset.formset.prefix }}",
- addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}",
- formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
- deleteCssClass: "inline-deletelink",
- deleteText: "{% trans "Remove" %}",
- emptyCssClass: "empty-form",
- removed: alternatingRows,
- added: (function(row) {
- initPrepopulatedFields(row);
- reinitDateTimeShortCuts();
- updateSelectFilter();
- alternatingRows(row);
- })
- });
- });
+ $("#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr").tabularFormset({
+ prefix: "{{ inline_admin_formset.formset.prefix }}",
+ adminStaticPrefix: '{% static "admin/" %}',
+ addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
+ deleteText: "{% trans 'Remove' %}"
+ });
})(django.jQuery);
</script>
2  django/contrib/admin/templatetags/admin_list.py
View
@@ -182,7 +182,7 @@ def items_for_result(cl, result, form):
row_class = ''
try:
f, attr, value = lookup_field(field_name, result, cl.model_admin)
- except (AttributeError, ObjectDoesNotExist):
+ except ObjectDoesNotExist:
result_repr = EMPTY_CHANGELIST_VALUE
else:
if f is None:
2  django/contrib/admin/util.py
View
@@ -4,7 +4,7 @@
import decimal
from django.db import models
-from django.db.models.sql.constants import LOOKUP_SEP
+from django.db.models.constants import LOOKUP_SEP
from django.db.models.deletion import Collector
from django.db.models.related import RelatedObject
from django.forms.forms import pretty_name
11 django/contrib/auth/decorators.py
View
@@ -8,6 +8,7 @@
from django.core.exceptions import PermissionDenied
from django.utils.decorators import available_attrs
from django.utils.encoding import force_str
+from django.shortcuts import resolve_url
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
@@ -23,17 +24,19 @@ def _wrapped_view(request, *args, **kwargs):
if test_func(request.user):
return view_func(request, *args, **kwargs)
path = request.build_absolute_uri()
- # urlparse chokes on lazy objects in Python 3
- login_url_as_str = force_str(login_url or settings.LOGIN_URL)
+ # urlparse chokes on lazy objects in Python 3, force to str
+ resolved_login_url = force_str(
+ resolve_url(login_url or settings.LOGIN_URL))
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
- login_scheme, login_netloc = urlparse(login_url_as_str)[:2]
+ login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
- return redirect_to_login(path, login_url, redirect_field_name)
+ return redirect_to_login(
+ path, resolved_login_url, redirect_field_name)
return _wrapped_view
return decorator
28 django/contrib/auth/forms.py
View
@@ -11,7 +11,7 @@
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
-from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, identify_hasher
+from django.contrib.auth.hashers import UNUSABLE_PASSWORD, identify_hasher
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import get_current_site
@@ -24,22 +24,22 @@
class ReadOnlyPasswordHashWidget(forms.Widget):
def render(self, name, value, attrs):
encoded = value
-
- if not is_password_usable(encoded):
- return "None"
-
final_attrs = self.build_attrs(attrs)
- try:
- hasher = identify_hasher(encoded)
- except ValueError:
- summary = mark_safe("<strong>Invalid password format or unknown hashing algorithm.</strong>")
+ if encoded == '' or encoded == UNUSABLE_PASSWORD:
+ summary = mark_safe("<strong>%s</strong>" % ugettext("No password set."))
else:
- summary = format_html_join('',
- "<strong>{0}</strong>: {1} ",
- ((ugettext(key), value)
- for key, value in hasher.safe_summary(encoded).items())
- )
+ try:
+ hasher = identify_hasher(encoded)
+ except ValueError:
+ summary = mark_safe("<strong>%s</strong>" % ugettext(
+ "Invalid password format or unknown hashing algorithm."))
+ else:
+ summary = format_html_join('',
+ "<strong>{0}</strong>: {1} ",
+ ((ugettext(key), value)
+ for key, value in hasher.safe_summary(encoded).items())
+ )
return format_html("<div{0}>{1}</div>", flatatt(final_attrs), summary)
8 django/contrib/auth/hashers.py
View
@@ -28,7 +28,13 @@ def reset_hashers(**kwargs):
def is_password_usable(encoded):
- return (encoded is not None and encoded != UNUSABLE_PASSWORD)
+ if encoded is None or encoded == UNUSABLE_PASSWORD:
+ return False
+ try:
+ hasher = identify_hasher(encoded)
+ except ValueError:
+ return False
+ return True
def check_password(password, encoded, setter=None, preferred='default'):
2  django/contrib/auth/models.py
View
@@ -25,7 +25,7 @@ def update_last_login(sender, user, **kwargs):
the user logging in.
"""
user.last_login = timezone.now()
- user.save()
+ user.save(update_fields=['last_login'])
user_logged_in.connect(update_last_login)
2  django/contrib/auth/tests/decorators.py
View
@@ -25,7 +25,7 @@ def normal_view(request):
pass
login_required(normal_view)
- def testLoginRequired(self, view_url='/login_required/', login_url=settings.LOGIN_URL):
+ def testLoginRequired(self, view_url='/login_required/', login_url='/login/'):
"""
Check that login_required works on a simple view wrapped in a
login_required decorator.
18 django/contrib/auth/tests/forms.py
View
@@ -236,23 +236,29 @@ class Meta(UserChangeForm.Meta):
# Just check we can create it
form = MyUserForm({})
+ def test_unsuable_password(self):
+ user = User.objects.get(username='empty_password')
+ user.set_unusable_password()
+ user.save()
+ form = UserChangeForm(instance=user)
+ self.assertIn(_("No password set."), form.as_table())
+
def test_bug_17944_empty_password(self):
user = User.objects.get(username='empty_password')
form = UserChangeForm(instance=user)
- # Just check that no error is raised.
- form.as_table()
+ self.assertIn(_("No password set."), form.as_table())
def test_bug_17944_unmanageable_password(self):
user = User.objects.get(username='unmanageable_password')
form = UserChangeForm(instance=user)
- # Just check that no error is raised.
- form.as_table()
+ self.assertIn(_("Invalid password format or unknown hashing algorithm."),
+ form.as_table())
def test_bug_17944_unknown_password_algorithm(self):
user = User.objects.get(username='unknown_password')
form = UserChangeForm(instance=user)
- # Just check that no error is raised.
- form.as_table()
+ self.assertIn(_("Invalid password format or unknown hashing algorithm."),
+ form.as_table())
@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
4 django/contrib/auth/tests/hashers.py
View
@@ -100,6 +100,10 @@ def doit():
self.assertRaises(ValueError, doit)
self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash")
+ def test_bad_encoded(self):
+ self.assertFalse(is_password_usable('letmein_badencoded'))
+ self.assertFalse(is_password_usable(''))
+
def test_low_level_pkbdf2(self):
hasher = PBKDF2PasswordHasher()
encoded = hasher.encode('letmein', 'seasalt')
5 django/contrib/auth/tests/views.py
View
@@ -1,7 +1,7 @@
import os
import re
-from django.conf import settings
+from django.conf import global_settings, settings
from django.contrib.sites.models import Site, RequestSite
from django.contrib.auth.models import User
from django.core import mail
@@ -23,7 +23,8 @@
('en', 'English'),
),
LANGUAGE_CODE='en',
- TEMPLATE_DIRS = (
+ TEMPLATE_LOADERS=global_settings.TEMPLATE_LOADERS,
+ TEMPLATE_DIRS=(
os.path.join(os.path.dirname(__file__), 'templates'),
),
USE_TZ=False,
18 django/contrib/auth/views.py
View
@@ -7,9 +7,9 @@
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, QueryDict
from django.template.response import TemplateResponse
-from django.utils.encoding import force_str
from django.utils.http import base36_to_int
from django.utils.translation import ugettext as _
+from django.shortcuts import resolve_url
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
@@ -38,16 +38,16 @@ def login(request, template_name='registration/login.html',
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
- netloc = urlparse(redirect_to)[1]
-
# Use default setting if redirect_to is empty
if not redirect_to:
redirect_to = settings.LOGIN_REDIRECT_URL
+ redirect_to = resolve_url(redirect_to)
+ netloc = urlparse(redirect_to)[1]
# Heavier security check -- don't allow redirection to a different
# host.
- elif netloc and netloc != request.get_host():
- redirect_to = settings.LOGIN_REDIRECT_URL
+ if netloc and netloc != request.get_host():
+ redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
# Okay, security checks complete. Log the user in.
auth_login(request, form.get_user())
@@ -110,6 +110,7 @@ def logout_then_login(request, login_url=None, current_app=None, extra_context=N
"""
if not login_url:
login_url = settings.LOGIN_URL
+ login_url = resolve_url(login_url)
return logout(request, login_url, current_app=current_app, extra_context=extra_context)
def redirect_to_login(next, login_url=None,
@@ -117,10 +118,9 @@ def redirect_to_login(next, login_url=None,
"""
Redirects the user to the login page, passing the given 'next' page
"""
- # urlparse chokes on lazy objects in Python 3
- login_url_as_str = force_str(login_url or settings.LOGIN_URL)
+ resolved_url = resolve_url(login_url or settings.LOGIN_URL)
- login_url_parts = list(urlparse(login_url_as_str))
+ login_url_parts = list(urlparse(resolved_url))
if redirect_field_name:
querystring = QueryDict(login_url_parts[4], mutable=True)
querystring[redirect_field_name] = next
@@ -229,7 +229,7 @@ def password_reset_complete(request,
template_name='registration/password_reset_complete.html',
current_app=None, extra_context=None):
context = {
- 'login_url': settings.LOGIN_URL
+ 'login_url': resolve_url(settings.LOGIN_URL)
}
if extra_context is not None:
context.update(extra_context)
2  django/contrib/gis/db/backends/base.py
View
@@ -90,8 +90,6 @@ def convert_geom(self, geom_val, geom_field):
# For quoting column values, rather than columns.
def geo_quote_name(self, name):
- if isinstance(name, six.text_type):
- name = name.encode('ascii')
return "'%s'" % name
# GeometryField operations
14 django/contrib/gis/db/backends/util.py
View
@@ -3,20 +3,6 @@
backends.
"""
-from django.utils import six
-
-def gqn(val):
- """
- The geographic quote name function; used for quoting tables and
- geometries (they use single rather than the double quotes of the
- backend quotename function).
- """
- if isinstance(val, six.string_types):
- if isinstance(val, six.text_type): val = val.encode('ascii')
- return "'%s'" % val
- else:
- return str(val)
-
class SpatialOperation(object):
"""
Base class for generating spatial SQL.
2  django/contrib/gis/db/models/sql/where.py
View
@@ -1,5 +1,5 @@
+from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import FieldDoesNotExist
-from django.db.models.sql.constants import LOOKUP_SEP
from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.where import Constraint, WhereNode
from django.contrib.gis.db.models.fields import GeometryField
6 django/contrib/gis/gdal/tests/test_ds.py
View
@@ -181,7 +181,11 @@ def test05_geometries(self):
# Making sure the SpatialReference is as expected.
if hasattr(source, 'srs_wkt'):
- self.assertEqual(source.srs_wkt, g.srs.wkt)
+ self.assertEqual(
+ source.srs_wkt,
+ # Depending on lib versions, WGS_84 might be WGS_1984
+ g.srs.wkt.replace('SPHEROID["WGS_84"', 'SPHEROID["WGS_1984"')
+ )
def test06_spatial_filter(self):
"Testing the Layer.spatial_filter property."
6 django/contrib/gis/gdal/tests/test_geom.py
View
@@ -1,3 +1,4 @@
+import json
from binascii import b2a_hex
try:
from django.utils.six.moves import cPickle as pickle
@@ -111,8 +112,9 @@ def test01e_json(self):
for g in self.geometries.json_geoms:
geom = OGRGeometry(g.wkt)
if not hasattr(g, 'not_equal'):
- self.assertEqual(g.json, geom.json)
- self.assertEqual(g.json, geom.geojson)
+ # Loading jsons to prevent decimal differences
+ self.assertEqual(json.loads(g.json), json.loads(geom.json))
+ self.assertEqual(json.loads(g.json), json.loads(geom.geojson))
self.assertEqual(OGRGeometry(g.wkt), OGRGeometry(geom.json))
def test02_points(self):
2  django/contrib/gis/geos/libgeos.py
View
@@ -110,7 +110,7 @@ def geos_version_info():
is a release candidate (and what number release candidate), and the C API
version.
"""
- ver = geos_version()
+ ver = geos_version().decode()
m = version_regex.match(ver)
if not m: raise GEOSException('Could not parse version info string "%s"' % ver)
return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor'))
9 django/contrib/gis/geos/mutable_list.py
View
@@ -215,15 +215,18 @@ def reverse(self):
"Standard list reverse method"
self[:] = self[-1::-1]
- def sort(self, cmp=cmp, key=None, reverse=False):
+ def sort(self, cmp=None, key=None, reverse=False):
"Standard list sort method"
if key:
temp = [(key(v),v) for v in self]
- temp.sort(cmp=cmp, key=lambda x: x[0], reverse=reverse)
+ temp.sort(key=lambda x: x[0], reverse=reverse)
self[:] = [v[1] for v in temp]
else:
temp = list(self)
- temp.sort(cmp=cmp, reverse=reverse)
+ if cmp is not None:
+ temp.sort(cmp=cmp, reverse=reverse)
+ else:
+ temp.sort(reverse=reverse)
self[:] = temp
### Private routines ###
3  django/contrib/gis/geos/tests/__init__.py
View
@@ -16,7 +16,8 @@
def suite():
"Builds a test suite for the GEOS tests."
s = TestSuite()
- map(s.addTest, test_suites)
+ for suite in test_suites:
+ s.addTest(suite)
return s
def run(verbosity=1):
6 django/contrib/gis/geos/tests/test_geos.py
View
@@ -1,4 +1,5 @@
import ctypes
+import json
import random
from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry,
@@ -204,8 +205,9 @@ def test_json(self):
for g in self.geometries.json_geoms:
geom = GEOSGeometry(g.wkt)
if not hasattr(g, 'not_equal'):
- self.assertEqual(g.json, geom.json)
- self.assertEqual(g.json, geom.geojson)
+ # Loading jsons to prevent decimal differences
+ self.assertEqual(json.loads(g.json), json.loads(geom.json))
+ self.assertEqual(json.loads(g.json), json.loads(geom.geojson))
self.assertEqual(GEOSGeometry(g.wkt), GEOSGeometry(geom.json))
def test_fromfile(self):
16 django/contrib/gis/geos/tests/test_mutable_list.py
View
@@ -55,14 +55,14 @@ class ListMixinTest(unittest.TestCase):
def lists_of_len(self, length=None):
if length is None: length = self.limit
- pl = range(length)
+ pl = list(range(length))
return pl, self.listType(pl)
def limits_plus(self, b):
return range(-self.limit - b, self.limit + b)
def step_range(self):
- return range(-1 - self.limit, 0) + range(1, 1 + self.limit)
+ return list(range(-1 - self.limit, 0)) + list(range(1, 1 + self.limit))
def test01_getslice(self):
'Slice retrieval'
@@ -160,13 +160,13 @@ def test03_delslice(self):
del pl[i:j]
del ul[i:j]
self.assertEqual(pl[:], ul[:], 'del slice [%d:%d]' % (i,j))
- for k in range(-Len - 1,0) + range(1,Len):
+ for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
pl, ul = self.lists_of_len(Len)
del pl[i:j:k]
del ul[i:j:k]
self.assertEqual(pl[:], ul[:], 'del slice [%d:%d:%d]' % (i,j,k))
- for k in range(-Len - 1,0) + range(1,Len):
+ for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
pl, ul = self.lists_of_len(Len)
del pl[:i:k]
del ul[:i:k]
@@ -177,7 +177,7 @@ def test03_delslice(self):
del ul[i::k]
self.assertEqual(pl[:], ul[:], 'del slice [%d::%d]' % (i,k))
- for k in range(-Len - 1,0) + range(1,Len):
+ for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
pl, ul = self.lists_of_len(Len)
del pl[::k]
del ul[::k]
@@ -320,7 +320,7 @@ def test_11_sorting(self):
pl.sort()
ul.sort()
self.assertEqual(pl[:], ul[:], 'sort')
- mid = pl[len(pl) / 2]
+ mid = pl[len(pl) // 2]
pl.sort(key=lambda x: (mid-x)**2)
ul.sort(key=lambda x: (mid-x)**2)
self.assertEqual(pl[:], ul[:], 'sort w/ key')
@@ -330,7 +330,7 @@ def test_11_sorting(self):
pl.sort(reverse=True)
ul.sort(reverse=True)
self.assertEqual(pl[:], ul[:], 'sort w/ reverse')
- mid = pl[len(pl) / 2]
+ mid = pl[len(pl) // 2]
pl.sort(key=lambda x: (mid-x)**2)
ul.sort(key=lambda x: (mid-x)**2)
self.assertEqual(pl[:], ul[:], 'sort w/ key')
@@ -338,7 +338,7 @@ def test_11_sorting(self):
def test_12_arithmetic(self):
'Arithmetic'
pl, ul = self.lists_of_len()
- al = range(10,14)
+ al = list(range(10,14))
self.assertEqual(list(pl + al), list(ul + al), 'add')
self.assertEqual(type(ul), type(ul + al), 'type of add result')
self.assertEqual(list(al + pl), list(al + ul), 'radd')
3  django/contrib/gis/tests/geoapp/tests.py
View
@@ -191,7 +191,8 @@ def test_raw_sql_query(self):
cities1 = City.objects.all()
# Only PostGIS would support a 'select *' query because of its recognized
# HEXEWKB format for geometry fields
- cities2 = City.objects.raw('select id, name, asText(point) from geoapp_city')
+ as_text = 'ST_AsText' if postgis else 'asText'
+ cities2 = City.objects.raw('select id, name, %s(point) from geoapp_city' % as_text)
self.assertEqual(len(cities1), len(list(cities2)))
self.assertTrue(isinstance(cities2[0].point, Point))
32 django/contrib/gis/tests/test_spatialrefsys.py
View
@@ -8,9 +8,11 @@
test_srs = ({'srid' : 4326,
'auth_name' : ('EPSG', True),
'auth_srid' : 4326,
- 'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
- 'srtext14' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
- 'proj4' : '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
+ # Only the beginning, because there are differences depending on installed libs
+ 'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"',
+ 'proj4' : ['+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
+ # +ellps=WGS84 has been removed in the 4326 proj string in proj-4.8
+ '+proj=longlat +datum=WGS84 +no_defs '],
'spheroid' : 'WGS 84', 'name' : 'WGS 84',
'geographic' : True, 'projected' : False, 'spatialite' : True,
'ellipsoid' : (6378137.0, 6356752.3, 298.257223563), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
@@ -19,9 +21,9 @@
{'srid' : 32140,
'auth_name' : ('EPSG', False),
'auth_srid' : 32140,
- 'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',
- 'srtext14': 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],AUTHORITY["EPSG","32140"],AXIS["X",EAST],AXIS["Y",NORTH]]',
- 'proj4' : '+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
+ 'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980"',
+ 'proj4' : ['+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
+ '+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
'spheroid' : 'GRS 1980', 'name' : 'NAD83 / Texas South Central',
'geographic' : False, 'projected' : True, 'spatialite' : False,
'ellipsoid' : (6378137.0, 6356752.31414, 298.257222101), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
@@ -51,17 +53,12 @@ def test01_retrieve(self):
# No proj.4 and different srtext on oracle backends :(
if postgis:
- if connection.ops.spatial_version >= (1, 4, 0):
- srtext = sd['srtext14']
- else:
- srtext = sd['srtext']
- self.assertEqual(srtext, srs.wkt)
- self.assertEqual(sd['proj4'], srs.proj4text)
+ self.assertTrue(srs.wkt.startswith(sd['srtext']))
+ self.assertTrue(srs.proj4text in sd['proj4'])
@no_mysql
def test02_osr(self):
"Testing getting OSR objects from SpatialRefSys model objects."
- from django.contrib.gis.gdal import GDAL_VERSION
for sd in test_srs:
sr = SpatialRefSys.objects.get(srid=sd['srid'])
self.assertEqual(True, sr.spheroid.startswith(sd['spheroid']))
@@ -76,15 +73,10 @@ def test02_osr(self):
# Testing the SpatialReference object directly.
if postgis or spatialite:
srs = sr.srs
- if GDAL_VERSION <= (1, 8):
- self.assertEqual(sd['proj4'], srs.proj4)
+ self.assertTrue(srs.proj4 in sd['proj4'])
# No `srtext` field in the `spatial_ref_sys` table in SpatiaLite
if not spatialite:
- if connection.ops.spatial_version >= (1, 4, 0):
- srtext = sd['srtext14']
- else:
- srtext = sd['srtext']
- self.assertEqual(srtext, srs.wkt)
+ self.assertTrue(srs.wkt.startswith(sd['srtext']))
@no_mysql
def test03_ellipsoid(self):
6 django/contrib/markup/templatetags/markup.py
View
@@ -47,6 +47,9 @@ def markdown(value, arg=''):
they will be silently ignored.
"""
+ import warnings
+ warnings.warn('The markdown filter has been deprecated',
+ category=DeprecationWarning)
try:
import markdown
except ImportError:
@@ -72,6 +75,9 @@ def markdown(value, arg=''):
@register.filter(is_safe=True)
def restructuredtext(value):
+ import warnings
+ warnings.warn('The restructuredtext filter has been deprecated',
+ category=DeprecationWarning)
try:
from docutils.core import publish_parts
except ImportError:
11 django/contrib/markup/tests.py
View
@@ -1,7 +1,9 @@
# Quick tests for the markup templatetags (django.contrib.markup)
import re
+import warnings
from django.template import Template, Context
+from django import test
from django.utils import unittest
from django.utils.html import escape
@@ -21,7 +23,7 @@
except ImportError:
docutils = None
-class Templates(unittest.TestCase):
+class Templates(test.TestCase):
textile_content = """Paragraph 1
@@ -37,6 +39,13 @@ class Templates(unittest.TestCase):
.. _link: http://www.example.com/"""
+ def setUp(self):
+ self.save_warnings_state()
+ warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.contrib.markup')
+
+ def tearDown(self):
+ self.restore_warnings_state()
+
@unittest.skipUnless(textile, 'textile not installed')
def test_textile(self):
t = Template("{% load markup %}{{ textile_content|textile }}")
8 django/contrib/messages/storage/cookie.py
View
@@ -46,10 +46,10 @@ class CookieStorage(BaseStorage):
Stores messages in a cookie.
"""
cookie_name = 'messages'
- # We should be able to store 4K in a cookie, but Internet Explorer
- # imposes 4K as the *total* limit for a domain. To allow other
- # cookies, we go for 3/4 of 4K.
- max_cookie_size = 3072
+ # uwsgi's default configuration enforces a maximum size of 4kb for all the
+ # HTTP headers. In order to leave some room for other cookies and headers,
+ # restrict the session cookie to 1/2 of 4kb. See #18781.
+ max_cookie_size = 2048
not_finished = '__messagesnotfinished__'
def _get(self, *args, **kwargs):
10 django/contrib/messages/tests/base.py
View
@@ -152,7 +152,7 @@ def test_full_request_response_cycle(self):
cycle.
"""
data = {
- 'messages': ['Test message %d' % x for x in range(10)],
+ 'messages': ['Test message %d' % x for x in range(5)],
}
show_url = reverse('django.contrib.messages.tests.urls.show')
for level in ('debug', 'info', 'success', 'warning', 'error'):
@@ -170,7 +170,7 @@ def test_full_request_response_cycle(self):
@override_settings(MESSAGE_LEVEL=constants.DEBUG)
def test_with_template_response(self):
data = {
- 'messages': ['Test message %d' % x for x in range(10)],
+ 'messages': ['Test message %d' % x for x in range(5)],
}
show_url = reverse('django.contrib.messages.tests.urls.show_template_response')
for level in self.levels.keys():
@@ -194,7 +194,7 @@ def test_multiple_posts(self):
before a GET.
"""
data = {
- 'messages': ['Test message %d' % x for x in range(10)],
+ 'messages': ['Test message %d' % x for x in range(5)],
}
show_url = reverse('django.contrib.messages.tests.urls.show')
messages = []
@@ -226,7 +226,7 @@ def test_middleware_disabled(self):
when one attempts to store a message.
"""
data = {
- 'messages': ['Test message %d' % x for x in range(10)],
+ 'messages': ['Test message %d' % x for x in range(5)],
}
show_url = reverse('django.contrib.messages.tests.urls.show')
for level in ('debug', 'info', 'success', 'warning', 'error'):
@@ -251,7 +251,7 @@ def test_middleware_disabled_fail_silently(self):
raised if 'fail_silently' = True
"""
data = {
- 'messages': ['Test message %d' % x for x in range(10)],
+ 'messages': ['Test message %d' % x for x in range(5)],
'fail_silently': True,
}
show_url = reverse('django.contrib.messages.tests.urls.show')
14 django/contrib/sessions/tests.py
View
@@ -1,4 +1,4 @@
-from datetime import datetime, timedelta
+from datetime import timedelta
import shutil
import string
import tempfile
@@ -302,11 +302,11 @@ def test_exists_searches_cache_first(self):
self.assertTrue(self.session.exists(self.session.session_key))
def test_load_overlong_key(self):
- with warnings.catch_warnings(record=True) as w:
- warnings.simplefilter("always")
+ # Some backends might issue a warning
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
self.session._session_key = (string.ascii_letters + string.digits) * 20
self.assertEqual(self.session.load(), {})
- self.assertEqual(len(w), 1)
@override_settings(USE_TZ=True)
@@ -352,11 +352,11 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
backend = CacheSession
def test_load_overlong_key(self):
- with warnings.catch_warnings(record=True) as w:
- warnings.simplefilter("always")
+ # Some backends might issue a warning
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
self.session._session_key = (string.ascii_letters + string.digits) * 20
self.assertEqual(self.session.load(), {})
- self.assertEqual(len(w), 1)
class SessionMiddlewareTests(unittest.TestCase):
23 django/core/handlers/wsgi.py
View
@@ -223,18 +223,17 @@ def __call__(self, environ, start_response):
set_script_prefix(base.get_script_name(environ))
signals.request_started.send(sender=self.__class__)
try:
- try:
- request = self.request_class(environ)
- except UnicodeDecodeError:
- logger.warning('Bad Request (UnicodeDecodeError)',
- exc_info=sys.exc_info(),
- extra={
- 'status_code': 400,
- }
- )
- response = http.HttpResponseBadRequest()
- else:
- response = self.get_response(request)
+ request = self.request_class(environ)
+ except UnicodeDecodeError:
+ logger.warning('Bad Request (UnicodeDecodeError)',
+ exc_info=sys.exc_info(),
+ extra={
+ 'status_code': 400,
+ }
+ )
+ response = http.HttpResponseBadRequest()
+ else:
+ response = self.get_response(request)
finally:
signals.request_finished.send(sender=self.__class__)
3  django/core/management/__init__.py
View
@@ -5,6 +5,7 @@
import imp
import warnings
+from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError, handle_default_options
from django.core.management.color import color_style
from django.utils.importlib import import_module
@@ -105,7 +106,7 @@ def get_commands():
try:
from django.conf import settings
apps = settings.INSTALLED_APPS
- except (AttributeError, EnvironmentError, ImportError):
+ except (AttributeError, ImproperlyConfigured):
apps = []
# Find and load the management module for each installed app.
3  django/core/management/commands/runserver.py
View
@@ -1,4 +1,5 @@
from optparse import make_option
+from datetime import datetime
import os
import re
import sys
@@ -90,10 +91,12 @@ def inner_run(self, *args, **options):
self.stdout.write("Validating models...\n\n")
self.validate(display_num_errors=True)
self.stdout.write((
+ "%(started_at)s\n"
"Django version %(version)s, using settings %(settings)r\n"
"Development server is running at http://%(addr)s:%(port)s/\n"
"Quit the server with %(quit_command)s.\n"
) % {
+ "started_at": datetime.now().strftime('%B %d, %Y - %X'),
"version": self.get_version(),
"settings": settings.SETTINGS_MODULE,
"addr": self._raw_ipv6 and '[%s]' % self.addr or self.addr,
7 django/db/backends/__init__.py
View
@@ -1054,9 +1054,12 @@ def get_key_columns(self, cursor, table_name):
def get_primary_key_column(self, cursor, table_name):
"""
- Backends can override this to return the column name of the primary key for the given table.
+ Returns the name of the primary key column for the given table.
"""
- raise NotImplementedError
+ for column in six.iteritems(self.get_indexes(cursor, table_name)):
+ if column[1]['primary_key']:
+ return column[0]
+ return None
def get_indexes(self, cursor, table_name):
"""
3  django/db/backends/mysql/base.py
View
@@ -38,6 +38,7 @@
from django.db.backends.mysql.introspection import DatabaseIntrospection
from django.db.backends.mysql.validation import DatabaseValidation
from django.db.backends.mysql.schema import DatabaseSchemaEditor
+from django.utils.encoding import force_str
from django.utils.functional import cached_property
from django.utils.safestring import SafeBytes, SafeText
from django.utils import six
@@ -392,7 +393,7 @@ def _cursor(self):
if settings_dict['NAME']:
kwargs['db'] = settings_dict['NAME']
if settings_dict['PASSWORD']:
- kwargs['passwd'] = settings_dict['PASSWORD']
+ kwargs['passwd'] = force_str(settings_dict['PASSWORD'])
if settings_dict['HOST'].startswith('/'):
kwargs['unix_socket'] = settings_dict['HOST']
elif settings_dict['HOST']:
10 django/db/backends/mysql/introspection.py
View
@@ -2,7 +2,6 @@
from .base import FIELD_TYPE
from django.db.backends import BaseDatabaseIntrospection
-from django.utils import six
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
@@ -88,15 +87,6 @@ def get_key_columns(self, cursor, table_name):
key_columns.extend(cursor.fetchall())
return key_columns
- def get_primary_key_column(self, cursor, table_name):
- """
- Returns the name of the primary key column for the given table
- """
- for column in six.iteritems(self.get_indexes(cursor, table_name)):
- if column[1]['primary_key']:
- return column[0]
- return None
-
def get_indexes(self, cursor, table_name):
cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name))
# Do a two-pass search for indexes: on first pass check which indexes
3  django/db/backends/postgresql_psycopg2/base.py
View
@@ -14,6 +14,7 @@
from django.db.backends.postgresql_psycopg2.version import get_version
from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
+from django.utils.encoding import force_str
from django.utils.log import getLogger
from django.utils.safestring import SafeText, SafeBytes
from django.utils import six
@@ -175,7 +176,7 @@ def _cursor(self):
if settings_dict['USER']:
conn_params['user'] = settings_dict['USER']
if settings_dict['PASSWORD']:
- conn_params['password'] = settings_dict['PASSWORD']
+ conn_params['password'] = force_str(settings_dict['PASSWORD'])
if settings_dict['HOST']:
conn_params['host'] = settings_dict['HOST']
if settings_dict['PORT']:
7 django/db/models/constants.py
View
@@ -0,0 +1,7 @@
+"""
+Constants used across the ORM in general.
+"""
+
+# Separator used to split filter strings apart.
+LOOKUP_SEP = '__'
+
3  django/db/models/query.py
View
@@ -8,6 +8,7 @@
from django.core import exceptions
from django.db import connections, router, transaction, IntegrityError
+from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import AutoField
from django.db.models.query_utils import (Q, select_related_descend,
deferred_class_factory, InvalidQuery)
@@ -1613,8 +1614,6 @@ def prefetch_related_objects(result_cache, related_lookups):
Populates prefetched objects caches for a list of results
from a QuerySet
"""
- from django.db.models.sql.constants import LOOKUP_SEP
-
if len(result_cache) == 0:
return # nothing to do
9 django/db/models/sql/compiler.py
View
@@ -3,9 +3,10 @@
from django.core.exceptions import FieldError
from django.db import transaction
from django.db.backends.util import truncate_name
+from django.db.models.constants import LOOKUP_SEP
from django.db.models.query_utils import select_related_descend
from django.db.models.sql.constants import (SINGLE, MULTI, ORDER_DIR,
- LOOKUP_SEP, GET_ITERATOR_CHUNK_SIZE)
+ GET_ITERATOR_CHUNK_SIZE)
from django.db.models.sql.datastructures import EmptyResultSet
from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.query import get_order_dir, Query
@@ -608,8 +609,12 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
restricted = False
for f, model in opts.get_fields_with_model():
+ # The get_fields_with_model() returns None for fields that live
+ # in the field's local model. So, for those fields we want to use
+ # the f.model - that is the field's local model.
+ field_model = model or f.model
if not select_related_descend(f, restricted, requested,
- only_load.get(model or self.query.model)):
+ only_load.get(field_model)):
continue
# The "avoid" set is aliases we want to avoid just for this
# particular branch of the recursion. They aren't permanently
11 django/db/models/sql/constants.py
View
@@ -1,7 +1,13 @@
+"""
+Constants specific to the SQL storage portion of the ORM.
+"""
+
from collections import namedtuple
import re
-# Valid query types (a set is used for speedy lookups).
+# Valid query types (a set is used for speedy lookups). These are (currently)
+# considered SQL-specific; other storage systems may choose to use different
+# lookup types.
QUERY_TERMS = set([
'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in',
'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year',
@@ -12,9 +18,6 @@
# Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100
-# Separator used to split filter strings apart.
-LOOKUP_SEP = '__'
-
# Constants to make looking up tuple values clearer.
# Join lists (indexes into the tuples that are values in the alias_map
# dictionary in the Query class).
2  django/db/models/sql/expressions.py
View
@@ -1,6 +1,6 @@
from django.core.exceptions import FieldError
+from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import FieldDoesNotExist
-from django.db.models.sql.constants import LOOKUP_SEP
class SQLEvaluator(object):
def __init__(self, expression, query, allow_joins=True):
5 django/db/models/sql/query.py
View
@@ -15,11 +15,12 @@
from django.utils import six
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.models import signals
+from django.db.models.constants import LOOKUP_SEP
from django.db.models.expressions import ExpressionNode
from django.db.models.fields import FieldDoesNotExist
from django.db.models.sql import aggregates as base_aggregates_module
-from django.db.models.sql.constants import (QUERY_TERMS, LOOKUP_SEP, ORDER_DIR,
- SINGLE, ORDER_PATTERN, JoinInfo)
+from django.db.models.sql.constants import (QUERY_TERMS, ORDER_DIR, SINGLE,
+ ORDER_PATTERN, JoinInfo)
from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.where import (WhereNode, Constraint, EverythingNode,
1  django/db/models/sql/subqueries.py
View
@@ -3,6 +3,7 @@
"""
from django.core.exceptions import FieldError
+from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models.sql.constants import *
from django.db.models.sql.datastructures import Date
2  django/forms/fields.py
View
@@ -199,7 +199,7 @@ def to_python(self, value):
def widget_attrs(self, widget):
attrs = super(CharField, self).widget_attrs(widget)
- if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
+ if self.max_length is not None and isinstance(widget, TextInput):
# The HTML attribute is maxlength, not max_length.
attrs.update({'maxlength': str(self.max_length)})
return attrs
18 django/forms/widgets.py
View
@@ -260,10 +260,17 @@ def render(self, name, value, attrs=None):
final_attrs['value'] = force_text(self._format_value(value))
return format_html('<input{0} />', flatatt(final_attrs))
+
class TextInput(Input):
input_type = 'text'
-class PasswordInput(Input):
+ def __init__(self, attrs=None):
+ if attrs is not None:
+ self.input_type = attrs.pop('type', self.input_type)
+ super(TextInput, self).__init__(attrs)
+
+
+class PasswordInput(TextInput):
input_type = 'password'
def __init__(self, attrs=None, render_value=False):
@@ -400,9 +407,8 @@ def render(self, name, value, attrs=None):
flatatt(final_attrs),
force_text(value))
-class DateInput(Input):
- input_type = 'text'
+class DateInput(TextInput):
def __init__(self, attrs=None, format=None):
super(DateInput, self).__init__(attrs)