From a2975cb083a01012eb0cb997f0f5b7c2263cfbba Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sat, 29 Apr 2017 19:00:21 -0400 Subject: [PATCH] [1.11.x] Fixed #28040 -- Updated SplitArrayWidget to use template-based widget rendering. Thanks Preston Timmons for review. Backport of 1ebd295082bb0e6b230cf3bc39fd04bee71c2bd7 from master --- django/contrib/postgres/forms/array.py | 17 ++--- .../jinja2/postgres/widgets/split_array.html | 1 + .../postgres/widgets/split_array.html | 1 + docs/releases/1.11.1.txt | 3 + tests/postgres_tests/__init__.py | 11 +++- tests/postgres_tests/test_array.py | 64 ++++++++++++++++++- 6 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 django/contrib/postgres/jinja2/postgres/widgets/split_array.html create mode 100644 django/contrib/postgres/templates/postgres/widgets/split_array.html diff --git a/django/contrib/postgres/forms/array.py b/django/contrib/postgres/forms/array.py index 9830c8de48..4fbeca8877 100644 --- a/django/contrib/postgres/forms/array.py +++ b/django/contrib/postgres/forms/array.py @@ -7,7 +7,6 @@ from django.contrib.postgres.validators import ( ) from django.core.exceptions import ValidationError from django.utils import six -from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ from ..utils import prefix_validation_error @@ -91,6 +90,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 @@ -117,11 +117,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(SplitArrayWidget, self).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)): @@ -131,11 +133,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 dbb913dbba..75286781dd 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(PostgreSQLTestCase, cls).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)