Fixed #28040 -- Updated SplitArrayWidget to use template-based widget rendering.
Thanks Preston Timmons for review.
This commit is contained in:
parent
c920db1e57
commit
1ebd295082
|
@ -6,7 +6,6 @@ from django.contrib.postgres.validators import (
|
||||||
ArrayMaxLengthValidator, ArrayMinLengthValidator,
|
ArrayMaxLengthValidator, ArrayMinLengthValidator,
|
||||||
)
|
)
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from ..utils import prefix_validation_error
|
from ..utils import prefix_validation_error
|
||||||
|
@ -90,6 +89,7 @@ class SimpleArrayField(forms.CharField):
|
||||||
|
|
||||||
|
|
||||||
class SplitArrayWidget(forms.Widget):
|
class SplitArrayWidget(forms.Widget):
|
||||||
|
template_name = 'postgres/widgets/split_array.html'
|
||||||
|
|
||||||
def __init__(self, widget, size, **kwargs):
|
def __init__(self, widget, size, **kwargs):
|
||||||
self.widget = widget() if isinstance(widget, type) else widget
|
self.widget = widget() if isinstance(widget, type) else widget
|
||||||
|
@ -116,11 +116,13 @@ class SplitArrayWidget(forms.Widget):
|
||||||
id_ += '_0'
|
id_ += '_0'
|
||||||
return id_
|
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:
|
if self.is_localized:
|
||||||
self.widget.is_localized = self.is_localized
|
self.widget.is_localized = self.is_localized
|
||||||
value = value or []
|
value = value or []
|
||||||
output = []
|
context['widget']['subwidgets'] = []
|
||||||
final_attrs = self.build_attrs(attrs)
|
final_attrs = self.build_attrs(attrs)
|
||||||
id_ = final_attrs.get('id')
|
id_ = final_attrs.get('id')
|
||||||
for i in range(max(len(value), self.size)):
|
for i in range(max(len(value), self.size)):
|
||||||
|
@ -130,11 +132,10 @@ class SplitArrayWidget(forms.Widget):
|
||||||
widget_value = None
|
widget_value = None
|
||||||
if id_:
|
if id_:
|
||||||
final_attrs = dict(final_attrs, id='%s_%s' % (id_, i))
|
final_attrs = dict(final_attrs, id='%s_%s' % (id_, i))
|
||||||
output.append(self.widget.render(name + '_%s' % i, widget_value, final_attrs, renderer))
|
context['widget']['subwidgets'].append(
|
||||||
return mark_safe(self.format_output(output))
|
self.widget.get_context(name + '_%s' % i, widget_value, final_attrs)['widget']
|
||||||
|
)
|
||||||
def format_output(self, rendered_widgets):
|
return context
|
||||||
return ''.join(rendered_widgets)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media(self):
|
def media(self):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{% include 'django/forms/widgets/multiwidget.html' %}
|
|
@ -0,0 +1 @@
|
||||||
|
{% include 'django/forms/widgets/multiwidget.html' %}
|
|
@ -63,3 +63,6 @@ Bugfixes
|
||||||
that have initial data (:ticket:`28130`).
|
that have initial data (:ticket:`28130`).
|
||||||
|
|
||||||
* Prepared for ``cx_Oracle`` 6.0 support (:ticket:`28138`).
|
* Prepared for ``cx_Oracle`` 6.0 support (:ticket:`28138`).
|
||||||
|
|
||||||
|
* Updated the ``contrib.postgres`` ``SplitArrayWidget`` to use template-based
|
||||||
|
widget rendering (:ticket:`28040`).
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from forms_tests.widget_tests.base import WidgetTest
|
||||||
|
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.db.backends.signals import connection_created
|
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")
|
@unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific tests")
|
||||||
|
@ -14,3 +16,10 @@ class PostgreSQLTestCase(TestCase):
|
||||||
|
|
||||||
connection_created.disconnect(register_hstore_handler)
|
connection_created.disconnect(register_hstore_handler)
|
||||||
super().tearDownClass()
|
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
|
||||||
|
|
|
@ -8,11 +8,11 @@ from django.core import exceptions, serializers, validators
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.db import IntegrityError, connection, models
|
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.test.utils import isolate_apps
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from . import PostgreSQLTestCase
|
from . import PostgreSQLTestCase, PostgreSQLWidgetTestCase
|
||||||
from .models import (
|
from .models import (
|
||||||
ArrayFieldSubclass, CharArrayModel, DateTimeArrayModel, IntegerArrayModel,
|
ArrayFieldSubclass, CharArrayModel, DateTimeArrayModel, IntegerArrayModel,
|
||||||
NestedIntegerArrayModel, NullableIntegerArrayModel, OtherTypesArrayModel,
|
NestedIntegerArrayModel, NullableIntegerArrayModel, OtherTypesArrayModel,
|
||||||
|
@ -749,6 +749,8 @@ class TestSplitFormField(PostgreSQLTestCase):
|
||||||
with self.assertRaisesMessage(exceptions.ValidationError, msg):
|
with self.assertRaisesMessage(exceptions.ValidationError, msg):
|
||||||
SplitArrayField(forms.IntegerField(max_value=100), size=2).clean([0, 101])
|
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):
|
def test_rendering(self):
|
||||||
class SplitForm(forms.Form):
|
class SplitForm(forms.Form):
|
||||||
array = SplitArrayField(forms.CharField(), size=3)
|
array = SplitArrayField(forms.CharField(), size=3)
|
||||||
|
@ -787,7 +789,63 @@ class TestSplitFormField(PostgreSQLTestCase):
|
||||||
self.assertEqual(obj.field, [1, 2])
|
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,
|
||||||
|
"""
|
||||||
|
<input name="array_0" type="text" />
|
||||||
|
<input name="array_1" type="text" />
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_render_attrs(self):
|
||||||
|
self.check_html(
|
||||||
|
SplitArrayWidget(forms.TextInput(), size=2),
|
||||||
|
'array', ['val1', 'val2'], attrs={'id': 'foo'},
|
||||||
|
html=(
|
||||||
|
"""
|
||||||
|
<input id="foo_0" name="array_0" type="text" value="val1" />
|
||||||
|
<input id="foo_1" name="array_1" type="text" value="val2" />
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def test_value_omitted_from_data(self):
|
def test_value_omitted_from_data(self):
|
||||||
widget = SplitArrayWidget(forms.TextInput(), size=2)
|
widget = SplitArrayWidget(forms.TextInput(), size=2)
|
||||||
|
|
Loading…
Reference in New Issue