{% endif %}
{% if show_save_as_new %}{%endif%}
-{% if show_save_and_add_another %}{% endif %}
+{% if show_save_and_add_another %}{% endif %}
{% if show_save_and_continue %}{% endif %}
diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
index c190533f956..f6ac59635a6 100644
--- a/django/contrib/admin/templatetags/admin_modify.py
+++ b/django/contrib/admin/templatetags/admin_modify.py
@@ -28,7 +28,8 @@ def submit_row(context):
change = context['change']
is_popup = context['is_popup']
save_as = context['save_as']
- return {
+ ctx = {
+ 'opts': opts,
'onclick_attrib': (opts.get_ordered_objects() and change
and 'onclick="submitOrderForm();"' or ''),
'show_delete_link': (not is_popup and context['has_delete_permission']
@@ -40,6 +41,9 @@ def submit_row(context):
'is_popup': is_popup,
'show_save': True
}
+ if context.get('original') is not None:
+ ctx['original'] = context['original']
+ return ctx
@register.filter
def cell_count(inline_admin_form):
diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
index f95fe53de18..74eef2e7338 100644
--- a/django/contrib/admin/util.py
+++ b/django/contrib/admin/util.py
@@ -48,9 +48,9 @@ def prepare_lookup_value(key, value):
def quote(s):
"""
Ensure that primary key values do not confuse the admin URLs by escaping
- any '/', '_' and ':' characters. Similar to urllib.quote, except that the
- quoting is slightly different so that it doesn't get automatically
- unquoted by the Web browser.
+ any '/', '_' and ':' and similarly problematic characters.
+ Similar to urllib.quote, except that the quoting is slightly different so
+ that it doesn't get automatically unquoted by the Web browser.
"""
if not isinstance(s, six.string_types):
return s
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index 74ef095b4bc..5033ba98bc8 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -3,6 +3,7 @@ from functools import reduce
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
from django.core.paginator import InvalidPage
+from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.fields import FieldDoesNotExist
from django.utils.datastructures import SortedDict
@@ -376,4 +377,8 @@ class ChangeList(object):
return qs
def url_for_result(self, result):
- return "%s/" % quote(getattr(result, self.pk_attname))
+ pk = getattr(result, self.pk_attname)
+ return reverse('admin:%s_%s_change' % (self.opts.app_label,
+ self.opts.module_name),
+ args=(quote(pk),),
+ current_app=self.model_admin.admin_site.name)
diff --git a/django/contrib/formtools/tests/__init__.py b/django/contrib/formtools/tests/__init__.py
index 15941332edd..a21ffde533a 100644
--- a/django/contrib/formtools/tests/__init__.py
+++ b/django/contrib/formtools/tests/__init__.py
@@ -12,6 +12,7 @@ from django.conf import settings
from django.contrib.formtools import preview, utils
from django.contrib.formtools.wizard import FormWizard
from django.test import TestCase
+from django.test.html import parse_html
from django.test.utils import override_settings
from django.utils import unittest
@@ -218,7 +219,6 @@ class DummyRequest(http.HttpRequest):
)
class WizardTests(TestCase):
urls = 'django.contrib.formtools.tests.urls'
- input_re = re.compile('name="([^"]+)" value="([^"]+)"')
wizard_step_data = (
{
'0-name': 'Pony',
@@ -409,14 +409,13 @@ class WizardTests(TestCase):
"""
Pull the appropriate field data from the context to pass to the next wizard step
"""
- previous_fields = response.context['previous_fields']
+ previous_fields = parse_html(response.context['previous_fields'])
fields = {'wizard_step': response.context['step0']}
- def grab(m):
- fields[m.group(1)] = m.group(2)
- return ''
+ for input_field in previous_fields:
+ input_attrs = dict(input_field.attributes)
+ fields[input_attrs["name"]] = input_attrs["value"]
- self.input_re.sub(grab, previous_fields)
return fields
def check_wizard_step(self, response, step_no):
@@ -428,7 +427,6 @@ class WizardTests(TestCase):
"""
step_count = len(self.wizard_step_data)
- self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Step %d of %d' % (step_no, step_count))
data = self.grab_field_data(response)
diff --git a/django/contrib/formtools/tests/wizard/cookiestorage.py b/django/contrib/formtools/tests/wizard/cookiestorage.py
index 495d3afd033..d450f478614 100644
--- a/django/contrib/formtools/tests/wizard/cookiestorage.py
+++ b/django/contrib/formtools/tests/wizard/cookiestorage.py
@@ -1,3 +1,5 @@
+import json
+
from django.test import TestCase
from django.core import signing
from django.core.exceptions import SuspiciousOperation
@@ -41,4 +43,5 @@ class TestCookieStorage(TestStorage, TestCase):
storage.init_data()
storage.update_response(response)
unsigned_cookie_data = cookie_signer.unsign(response.cookies[storage.prefix].value)
- self.assertEqual(unsigned_cookie_data, '{"step_files":{},"step":null,"extra_data":{},"step_data":{}}')
+ self.assertEqual(json.loads(unsigned_cookie_data),
+ {"step_files": {}, "step": None, "extra_data": {}, "step_data": {}})
diff --git a/django/contrib/gis/gdal/__init__.py b/django/contrib/gis/gdal/__init__.py
index f477f05982f..de41df90ff0 100644
--- a/django/contrib/gis/gdal/__init__.py
+++ b/django/contrib/gis/gdal/__init__.py
@@ -41,7 +41,7 @@ try:
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
from django.contrib.gis.gdal.geometries import OGRGeometry
HAS_GDAL = True
-except ImportError:
+except Exception:
HAS_GDAL = False
try:
diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
index e80ac85bd87..2f95c33dd5d 100644
--- a/docs/topics/cache.txt
+++ b/docs/topics/cache.txt
@@ -286,7 +286,7 @@ cache is multi-process and thread-safe. To use it, set
The cache :setting:`LOCATION ` is used to identify individual
memory stores. If you only have one locmem cache, you can omit the
-:setting:`LOCATION `; however, if you have more that one local
+:setting:`LOCATION `; however, if you have more than one local
memory cache, you will need to assign a name to at least one of them in
order to keep them separate.
diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
index 94236babd63..a4aae0bc029 100644
--- a/docs/topics/logging.txt
+++ b/docs/topics/logging.txt
@@ -345,36 +345,6 @@ This logging configuration does the following things:
printed to the console; ``ERROR`` and ``CRITICAL``
messages will also be output via email.
-.. admonition:: Custom handlers and circular imports
-
- If your ``settings.py`` specifies a custom handler class and the file
- defining that class also imports ``settings.py`` a circular import will
- occur.
-
- For example, if ``settings.py`` contains the following config for
- :setting:`LOGGING`::
-
- LOGGING = {
- 'version': 1,
- 'handlers': {
- 'custom_handler': {
- 'level': 'INFO',
- 'class': 'myproject.logconfig.MyHandler',
- }
- }
- }
-
- and ``myproject/logconfig.py`` has the following line before the
- ``MyHandler`` definition::
-
- from django.conf import settings
-
- then the ``dictconfig`` module will raise an exception like the following::
-
- ValueError: Unable to configure handler 'custom_handler':
- Unable to configure handler 'custom_handler':
- 'module' object has no attribute 'logconfig'
-
.. _formatter documentation: http://docs.python.org/library/logging.html#formatter-objects
Custom logging configuration
diff --git a/tests/regressiontests/admin_changelist/tests.py b/tests/regressiontests/admin_changelist/tests.py
index be88c9a1619..2b1c1a9bcfc 100644
--- a/tests/regressiontests/admin_changelist/tests.py
+++ b/tests/regressiontests/admin_changelist/tests.py
@@ -6,6 +6,7 @@ from django.contrib import admin
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR
from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse
from django.template import Context, Template
from django.test import TestCase
from django.test.client import RequestFactory
@@ -65,7 +66,8 @@ class ChangeListTests(TestCase):
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
context = Context({'cl': cl})
table_output = template.render(context)
- row_html = '
""" % (escape(quote(self.pk)), escape(self.pk))
+ "Link to the changeform of the object in changelist should use reverse() and be quoted -- #18072"
+ prefix = '/test_admin/admin/admin_views/modelwithstringprimarykey/'
+ response = self.client.get(prefix)
+ # this URL now comes through reverse(), thus iri_to_uri encoding
+ pk_final_url = escape(iri_to_uri(quote(self.pk)))
+ should_contain = """