Merge "Add additional attributes to choices in ChoiceField"
This commit is contained in:
commit
1558541985
@ -19,6 +19,7 @@ import netaddr
|
|||||||
from django.core.exceptions import ValidationError # noqa
|
from django.core.exceptions import ValidationError # noqa
|
||||||
from django.core import urlresolvers
|
from django.core import urlresolvers
|
||||||
from django.forms import fields
|
from django.forms import fields
|
||||||
|
from django.forms.util import flatatt # noqa
|
||||||
from django.forms import widgets
|
from django.forms import widgets
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.functional import Promise # noqa
|
from django.utils.functional import Promise # noqa
|
||||||
@ -123,7 +124,9 @@ class MultiIPField(IPField):
|
|||||||
|
|
||||||
class SelectWidget(widgets.Select):
|
class SelectWidget(widgets.Select):
|
||||||
"""Customizable select widget, that allows to render
|
"""Customizable select widget, that allows to render
|
||||||
data-xxx attributes from choices.
|
data-xxx attributes from choices. This widget also
|
||||||
|
allows user to specify additional html attributes
|
||||||
|
for choices.
|
||||||
|
|
||||||
.. attribute:: data_attrs
|
.. attribute:: data_attrs
|
||||||
|
|
||||||
@ -137,16 +140,54 @@ class SelectWidget(widgets.Select):
|
|||||||
|
|
||||||
A callable used to render the display value
|
A callable used to render the display value
|
||||||
from the option object.
|
from the option object.
|
||||||
|
|
||||||
|
.. attribute:: transform_html_attrs
|
||||||
|
|
||||||
|
A callable used to render additional HTML attributes
|
||||||
|
for the option object. It returns a dictionary
|
||||||
|
containing the html attributes and their values.
|
||||||
|
For example, to define a title attribute for the
|
||||||
|
choices:
|
||||||
|
|
||||||
|
helpText = { 'Apple': 'This is a fruit',
|
||||||
|
'Carrot': 'This is a vegetable' }
|
||||||
|
|
||||||
|
def get_title(data):
|
||||||
|
text = helpText.get(data, None)
|
||||||
|
if text:
|
||||||
|
return {'title': text}
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
....
|
||||||
|
....
|
||||||
|
|
||||||
|
widget=forms.SelectWidget( attrs={'class': 'switchable',
|
||||||
|
'data-slug': 'source'},
|
||||||
|
transform_html_attrs=get_title )
|
||||||
|
|
||||||
|
self.fields[<field name>].choices =
|
||||||
|
([
|
||||||
|
('apple','Apple'),
|
||||||
|
('carrot','Carrot')
|
||||||
|
])
|
||||||
"""
|
"""
|
||||||
def __init__(self, attrs=None, choices=(), data_attrs=(), transform=None):
|
def __init__(self, attrs=None, choices=(), data_attrs=(), transform=None,
|
||||||
|
transform_html_attrs=None):
|
||||||
self.data_attrs = data_attrs
|
self.data_attrs = data_attrs
|
||||||
self.transform = transform
|
self.transform = transform
|
||||||
|
self.transform_html_attrs = transform_html_attrs
|
||||||
super(SelectWidget, self).__init__(attrs, choices)
|
super(SelectWidget, self).__init__(attrs, choices)
|
||||||
|
|
||||||
def render_option(self, selected_choices, option_value, option_label):
|
def render_option(self, selected_choices, option_value, option_label):
|
||||||
option_value = force_text(option_value)
|
option_value = force_text(option_value)
|
||||||
other_html = (option_value in selected_choices) and \
|
other_html = (option_value in selected_choices) and \
|
||||||
u' selected="selected"' or ''
|
u' selected="selected"' or ''
|
||||||
|
|
||||||
|
if callable(self.transform_html_attrs):
|
||||||
|
html_attrs = self.transform_html_attrs(option_label)
|
||||||
|
other_html += flatatt(html_attrs)
|
||||||
|
|
||||||
if not isinstance(option_label, (basestring, Promise)):
|
if not isinstance(option_label, (basestring, Promise)):
|
||||||
for data_attr in self.data_attrs:
|
for data_attr in self.data_attrs:
|
||||||
data_value = html.conditional_escape(
|
data_value = html.conditional_escape(
|
||||||
@ -154,8 +195,9 @@ class SelectWidget(widgets.Select):
|
|||||||
data_attr, "")))
|
data_attr, "")))
|
||||||
other_html += ' data-%s="%s"' % (data_attr, data_value)
|
other_html += ' data-%s="%s"' % (data_attr, data_value)
|
||||||
|
|
||||||
if self.transform:
|
if callable(self.transform):
|
||||||
option_label = self.transform(option_label)
|
option_label = self.transform(option_label)
|
||||||
|
|
||||||
return u'<option value="%s"%s>%s</option>' % (
|
return u'<option value="%s"%s>%s</option>' % (
|
||||||
html.escape(option_value), other_html,
|
html.escape(option_value), other_html,
|
||||||
html.conditional_escape(force_text(option_label)))
|
html.conditional_escape(force_text(option_label)))
|
||||||
|
@ -98,3 +98,48 @@ class FormErrorTests(test.TestCase):
|
|||||||
self.assertEqual([error_text], self.form.non_field_errors())
|
self.assertEqual([error_text], self.form.non_field_errors())
|
||||||
resp = self._render_form()
|
resp = self._render_form()
|
||||||
self.assertIn(error_text, resp.content)
|
self.assertIn(error_text, resp.content)
|
||||||
|
|
||||||
|
|
||||||
|
class TestChoiceFieldForm(forms.SelfHandlingForm):
|
||||||
|
title_dic = {"label1": {"title": "This is choice 1"},
|
||||||
|
"label2": {"title": "This is choice 2"},
|
||||||
|
"label3": {"title": "This is choice 3"}}
|
||||||
|
name = forms.CharField(max_length=255,
|
||||||
|
label="Test Name",
|
||||||
|
help_text="Please enter a name")
|
||||||
|
test_choices = forms.ChoiceField(label="Test Choices",
|
||||||
|
required=False,
|
||||||
|
help_text="Testing drop down choices",
|
||||||
|
widget=forms.fields.SelectWidget(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'source'},
|
||||||
|
transform_html_attrs=title_dic.get))
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super(TestChoiceFieldForm, self).__init__(request, *args, **kwargs)
|
||||||
|
choices = ([('choice1', 'label1'),
|
||||||
|
('choice2', 'label2')])
|
||||||
|
self.fields['test_choices'].choices = choices
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ChoiceFieldTests(test.TestCase):
|
||||||
|
|
||||||
|
template = 'horizon/common/_form_fields.html'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ChoiceFieldTests, self).setUp()
|
||||||
|
self.form = TestChoiceFieldForm(self.request)
|
||||||
|
|
||||||
|
def _render_form(self):
|
||||||
|
return shortcuts.render(self.request, self.template,
|
||||||
|
{'form': self.form})
|
||||||
|
|
||||||
|
def test_choicefield_title(self):
|
||||||
|
resp = self._render_form()
|
||||||
|
self.assertContains(resp, '<option value="choice1" '
|
||||||
|
'title="This is choice 1">label1</option>', count=1, html=True)
|
||||||
|
self.assertContains(resp, '<option value="choice2" '
|
||||||
|
'title="This is choice 2">label2</option>', count=1, html=True)
|
||||||
|
Loading…
Reference in New Issue
Block a user