diff --git a/django/contrib/postgres/forms/array.py b/django/contrib/postgres/forms/array.py
index 303b46dfb2..97e4346f60 100644
--- a/django/contrib/postgres/forms/array.py
+++ b/django/contrib/postgres/forms/array.py
@@ -6,7 +6,6 @@ from django.contrib.postgres.validators import (
ArrayMaxLengthValidator, ArrayMinLengthValidator,
)
from django.core.exceptions import ValidationError
-from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from ..utils import prefix_validation_error
@@ -90,6 +89,7 @@ class SimpleArrayField(forms.CharField):
class SplitArrayWidget(forms.Widget):
+ template_name = 'postgres/widgets/split_array.html'
def __init__(self, widget, size, **kwargs):
self.widget = widget() if isinstance(widget, type) else widget
@@ -116,11 +116,13 @@ class SplitArrayWidget(forms.Widget):
id_ += '_0'
return id_
- def render(self, name, value, attrs=None, renderer=None):
+ def get_context(self, name, value, attrs=None):
+ attrs = {} if attrs is None else attrs
+ context = super().get_context(name, value, attrs)
if self.is_localized:
self.widget.is_localized = self.is_localized
value = value or []
- output = []
+ context['widget']['subwidgets'] = []
final_attrs = self.build_attrs(attrs)
id_ = final_attrs.get('id')
for i in range(max(len(value), self.size)):
@@ -130,11 +132,10 @@ class SplitArrayWidget(forms.Widget):
widget_value = None
if id_:
final_attrs = dict(final_attrs, id='%s_%s' % (id_, i))
- output.append(self.widget.render(name + '_%s' % i, widget_value, final_attrs, renderer))
- return mark_safe(self.format_output(output))
-
- def format_output(self, rendered_widgets):
- return ''.join(rendered_widgets)
+ context['widget']['subwidgets'].append(
+ self.widget.get_context(name + '_%s' % i, widget_value, final_attrs)['widget']
+ )
+ return context
@property
def media(self):
diff --git a/django/contrib/postgres/jinja2/postgres/widgets/split_array.html b/django/contrib/postgres/jinja2/postgres/widgets/split_array.html
new file mode 100644
index 0000000000..32fda82609
--- /dev/null
+++ b/django/contrib/postgres/jinja2/postgres/widgets/split_array.html
@@ -0,0 +1 @@
+{% include 'django/forms/widgets/multiwidget.html' %}
diff --git a/django/contrib/postgres/templates/postgres/widgets/split_array.html b/django/contrib/postgres/templates/postgres/widgets/split_array.html
new file mode 100644
index 0000000000..32fda82609
--- /dev/null
+++ b/django/contrib/postgres/templates/postgres/widgets/split_array.html
@@ -0,0 +1 @@
+{% include 'django/forms/widgets/multiwidget.html' %}
diff --git a/docs/releases/1.11.1.txt b/docs/releases/1.11.1.txt
index 08bf3e5135..fd54a603a2 100644
--- a/docs/releases/1.11.1.txt
+++ b/docs/releases/1.11.1.txt
@@ -63,3 +63,6 @@ Bugfixes
that have initial data (:ticket:`28130`).
* Prepared for ``cx_Oracle`` 6.0 support (:ticket:`28138`).
+
+* Updated the ``contrib.postgres`` ``SplitArrayWidget`` to use template-based
+ widget rendering (:ticket:`28040`).
diff --git a/tests/postgres_tests/__init__.py b/tests/postgres_tests/__init__.py
index 49f1220888..9396b7fe80 100644
--- a/tests/postgres_tests/__init__.py
+++ b/tests/postgres_tests/__init__.py
@@ -1,8 +1,10 @@
import unittest
+from forms_tests.widget_tests.base import WidgetTest
+
from django.db import connection
from django.db.backends.signals import connection_created
-from django.test import TestCase
+from django.test import TestCase, modify_settings
@unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific tests")
@@ -14,3 +16,10 @@ class PostgreSQLTestCase(TestCase):
connection_created.disconnect(register_hstore_handler)
super().tearDownClass()
+
+
+@unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific tests")
+# To locate the widget's template.
+@modify_settings(INSTALLED_APPS={'append': 'django.contrib.postgres'})
+class PostgreSQLWidgetTestCase(WidgetTest):
+ pass
diff --git a/tests/postgres_tests/test_array.py b/tests/postgres_tests/test_array.py
index a11b60e261..a66c92a016 100644
--- a/tests/postgres_tests/test_array.py
+++ b/tests/postgres_tests/test_array.py
@@ -8,11 +8,11 @@ from django.core import exceptions, serializers, validators
from django.core.exceptions import FieldError
from django.core.management import call_command
from django.db import IntegrityError, connection, models
-from django.test import TransactionTestCase, override_settings
+from django.test import TransactionTestCase, modify_settings, override_settings
from django.test.utils import isolate_apps
from django.utils import timezone
-from . import PostgreSQLTestCase
+from . import PostgreSQLTestCase, PostgreSQLWidgetTestCase
from .models import (
ArrayFieldSubclass, CharArrayModel, DateTimeArrayModel, IntegerArrayModel,
NestedIntegerArrayModel, NullableIntegerArrayModel, OtherTypesArrayModel,
@@ -749,6 +749,8 @@ class TestSplitFormField(PostgreSQLTestCase):
with self.assertRaisesMessage(exceptions.ValidationError, msg):
SplitArrayField(forms.IntegerField(max_value=100), size=2).clean([0, 101])
+ # To locate the widget's template.
+ @modify_settings(INSTALLED_APPS={'append': 'django.contrib.postgres'})
def test_rendering(self):
class SplitForm(forms.Form):
array = SplitArrayField(forms.CharField(), size=3)
@@ -787,7 +789,63 @@ class TestSplitFormField(PostgreSQLTestCase):
self.assertEqual(obj.field, [1, 2])
-class TestSplitFormWidget(PostgreSQLTestCase):
+class TestSplitFormWidget(PostgreSQLWidgetTestCase):
+
+ def test_get_context(self):
+ self.assertEqual(
+ SplitArrayWidget(forms.TextInput(), size=2).get_context('name', ['val1', 'val2']),
+ {
+ 'widget': {
+ 'name': 'name',
+ 'is_hidden': False,
+ 'required': False,
+ 'value': "['val1', 'val2']",
+ 'attrs': {},
+ 'template_name': 'postgres/widgets/split_array.html',
+ 'subwidgets': [
+ {
+ 'name': 'name_0',
+ 'is_hidden': False,
+ 'required': False,
+ 'value': 'val1',
+ 'attrs': {},
+ 'template_name': 'django/forms/widgets/text.html',
+ 'type': 'text',
+ },
+ {
+ 'name': 'name_1',
+ 'is_hidden': False,
+ 'required': False,
+ 'value': 'val2',
+ 'attrs': {},
+ 'template_name': 'django/forms/widgets/text.html',
+ 'type': 'text',
+ },
+ ]
+ }
+ }
+ )
+
+ def test_render(self):
+ self.check_html(
+ SplitArrayWidget(forms.TextInput(), size=2), 'array', None,
+ """
+
+
+ """
+ )
+
+ def test_render_attrs(self):
+ self.check_html(
+ SplitArrayWidget(forms.TextInput(), size=2),
+ 'array', ['val1', 'val2'], attrs={'id': 'foo'},
+ html=(
+ """
+
+
+ """
+ )
+ )
def test_value_omitted_from_data(self):
widget = SplitArrayWidget(forms.TextInput(), size=2)