diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index e9f4a43185..ce10cf72ba 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -24,7 +24,7 @@ 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 QUERY_TERMS
-from django.http import Http404, HttpResponse, HttpResponseRedirect
+from django.http import Http404, HttpResponseRedirect
from django.http.response import HttpResponseBase
from django.shortcuts import get_object_or_404
from django.template.response import SimpleTemplateResponse, TemplateResponse
@@ -911,11 +911,10 @@ class ModelAdmin(BaseModelAdmin):
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
if IS_POPUP_VAR in request.POST:
- return HttpResponse(
- '
'
- '' % \
- # escape() calls force_text.
- (escape(pk_value), escapejs(obj)))
+ return SimpleTemplateResponse('admin/popup_response.html', {
+ 'pk_value': escape(pk_value),
+ 'obj': escapejs(obj)
+ })
elif "_continue" in request.POST:
msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % msg_dict
diff --git a/django/contrib/admin/templates/admin/popup_response.html b/django/contrib/admin/templates/admin/popup_response.html
new file mode 100644
index 0000000000..44833b2f93
--- /dev/null
+++ b/django/contrib/admin/templates/admin/popup_response.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py
index 5cc6f6251a..5ec4fbb544 100644
--- a/tests/admin_views/models.py
+++ b/tests/admin_views/models.py
@@ -137,7 +137,7 @@ class Thing(models.Model):
class Actor(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
- title = models.CharField(max_length=50, null=True)
+ title = models.CharField(max_length=50, null=True, blank=True)
def __str__(self):
return self.name
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 528c728069..064979801e 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -2554,6 +2554,17 @@ action)
'/test_admin/admin/admin_views/subscriber/?%s' % IS_POPUP_VAR)
self.assertEqual(response.context["action_form"], None)
+ def test_popup_template_response(self):
+ """
+ Success on popups shall be rendered from template in order to allow
+ easy customization.
+ """
+ response = self.client.post(
+ '/test_admin/admin/admin_views/actor/add/?%s=1' % IS_POPUP_VAR,
+ {'name': 'Troy McClure', 'age': '55', IS_POPUP_VAR: '1'})
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.template_name, 'admin/popup_response.html')
+
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class TestCustomChangeList(TestCase):
@@ -4275,7 +4286,7 @@ class AdminKeepChangeListFiltersTests(TestCase):
# Get the `add_view`.
response = self.client.get(self.get_add_url())
self.assertEqual(response.status_code, 200)
-
+
# Check the form action.
form_action = """