PLANNING_CHOICES = (
('0',u'Every morning'),
('1',u'Every night'),
('2',u'Never'),
)
planning = forms.ChoiceField(
required=True,
choices = PLANNING_CHOICES,
)
有一个名为planning的表单字段,我需要在选项中添加title属性,最后呈现为:
<select>
<option value="0" title="bla1">Every morning</option>
<option value="1" title="bla2">Every night</option>
<option value="2" title="bla3">Never</option>
</select>
如何实现?
答案 0 :(得分:20)
您必须对该字段进行子类化,以采用任何方式指定您想要的标题,并使用小部件来显示新属性。
如果您有这样的事情(注意:完全未经测试):
from django import forms
from django.utils.html import escape
from django.utils.encoding import force_unicode
class SelectWithTitles(forms.Select):
def __init__(self, *args, **kwargs):
super(SelectWithTitles, self).__init__(*args, **kwargs)
# Ensure the titles dict exists
self.titles = {}
def render_option(self, selected_choices, option_value, option_label):
title_html = (option_label in self.titles) and \
u' title="%s" ' % escape(force_unicode(self.titles[option_label])) or ''
option_value = force_unicode(option_value)
selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
return u'<option value="%s"%s%s>%s</option>' % (
escape(option_value), title_html, selected_html,
conditional_escape(force_unicode(option_label)))
class ChoiceFieldWithTitles(forms.ChoiceField):
widget = SelectWithTitles
def __init__(self, choices=(), *args, **kwargs):
choice_pairs = [(c[0], c[1]) for c in choices]
super(ChoiceFieldWithTitles, self).__init__(choices=choice_pairs, *args, **kwargs)
self.widget.titles = dict([(c[1], c[2]) for c in choices])
......你应该能够做到这一点:
PLANNING_CHOICES_WITH_TITLES = (
('0', 'Every morning', 'bla1'),
('1', 'Every night', 'bla2'),
('2', 'Never', 'bla3'),
)
planning = forms.ChoiceFieldWithTitles(
required=True, choices=PLANNING_CHOICES_WITH_TITLES)
答案 1 :(得分:6)
这就是我解决问题的方法
#models.py
class Publisher(models.Model):
slug = models.Slugfield()
class Book(forms.ModelForm):
publisher = models.ForeignKey(Publisher)
#forms.py
from django import forms
from django.utils.encoding import force_unicode
from django.utils.html import escape, conditional_escape
class SelectWithData(forms.Select):
def render_option(self, selected_choices, option_value, option_label):
obj_data = {
obj.id: {
data_attr: getattr(obj, data_attr) for data_attr in self.data_attrs
} for obj in self.queryset
}
data_text = u''
for data_attr in self.data_attrs:
data_text += u' data-{}="{}" '.format(
data_attr,
escape(force_unicode(obj_data[option_value][data_attr]))
)
option_value = force_unicode(option_value)
selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
return u'<option value="{}"{}{}>{}</option>'.format(
escape(option_value),
data_text,
selected_html,
conditional_escape(force_unicode(option_label))
)
class ModelChoiceFieldWithData(forms.ModelChoiceField):
widget = SelectWithData
def __init__(self, queryset, **kwargs):
data_attrs = kwargs.pop('data_attrs')
super(ModelChoiceFieldWithData, self).__init__(queryset, **kwargs)
self.widget.queryset = queryset
self.widget.data_attrs = data_attrs
class BookForm(forms.ModelForm):
publisher = ModelChoiceFieldWithData(
queryset=Publisher.objects.all(),
data_attrs=('slug',),
)
#html
<select id="id_publisher" name="publisher" required="required" title="">
<option value="1" data-slug="first" selected="selected">First Publisher</option>
<option value="2" data-slug="second">Second Publisher</option>
</select>
答案 2 :(得分:3)
你做不到。至少,并非没有太多的hackery。
如果您想要对生成的标记进行更多控制,那么使用form_utils.BetterForm(外部包而非django.forms.Form
可能会很好。
答案 3 :(得分:1)
render_option
已从Django 1.11开始删除。这就是我为实现这一目标所做的。一点点挖掘,这看起来很简单。适用于Django 2.0 +
class CustomSelect(forms.Select):
def __init__(self, attrs=None, choices=()):
self.custom_attrs = {}
super().__init__(attrs, choices)
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
index = str(index) if subindex is None else "%s_%s" % (index, subindex)
if attrs is None:
attrs = {}
option_attrs = self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
if selected:
option_attrs.update(self.checked_attribute)
if 'id' in option_attrs:
option_attrs['id'] = self.id_for_label(option_attrs['id'], index)
# setting the attributes here for the option
if len(self.custom_attrs) > 0:
if value in self.custom_attrs:
custom_attr = self.custom_attrs[value]
for k, v in custom_attr.items():
option_attrs.update({k: v})
return {
'name': name,
'value': value,
'label': label,
'selected': selected,
'index': index,
'attrs': option_attrs,
'type': self.input_type,
'template_name': self.option_template_name,
}
class MyModelChoiceField(ModelChoiceField):
# custom method to label the option field
def label_from_instance(self, obj):
# since the object is accessible here you can set the extra attributes
if hasattr(obj, 'type'):
self.widget.custom_attrs.update({obj.pk: {'type': obj.type}})
return obj.get_display_name()
表格:
class BookingForm(forms.ModelForm):
customer = MyModelChoiceField(required=True,
queryset=Customer.objects.filter(is_active=True).order_by('name'),
widget=CustomSelect(attrs={'class': 'chosen-select'}))
我需要的输出是:
<select name="customer" class="chosen-select" required="" id="id_customer">
<option value="" selected="">---------</option>
<option value="242" type="CNT">AEC Transcolutions Private Limited</option>
<option value="243" type="CNT">BBC FREIGHT CARRIER</option>
<option value="244" type="CNT">Blue Dart Express Limited</option>
答案 4 :(得分:0)
这是允许在Select小部件,SelectMultiple小部件以及Select2小部件中的选项中使用属性的通用解决方案。
它已经在Django 2.1上进行了测试,并且也可以在其他版本上使用(在注释中告诉我)。
from django.forms.widgets import Select, SelectMultiple
class SelectWOA(Select):
"""
Select With Option Attributes:
subclass of Django's Select widget that allows attributes in options,
like disabled="disabled", title="help text", class="some classes",
style="background: color;"...
Pass a dict instead of a string for its label:
choices = [ ('value_1', 'label_1'),
...
('value_k', {'label': 'label_k', 'foo': 'bar', ...}),
... ]
The option k will be rendered as:
<option value="value_k" foo="bar" ...>label_k</option>
"""
def create_option(self, name, value, label, selected, index,
subindex=None, attrs=None):
if isinstance(label, dict):
opt_attrs = label.copy()
label = opt_attrs.pop('label')
else:
opt_attrs = {}
option_dict = super(SelectWOA, self).create_option(name, value,
label, selected, index, subindex=subindex, attrs=attrs)
for key,val in opt_attrs.items():
option_dict['attrs'][key] = val
return option_dict
以下是您可以在表格中尝试的示例。py:
choices = [('b', 'blue'),
('g', {'label': 'green', 'disabled': 'disabled'}),
('c', {'label': 'cyan',
'title': 'Kind of violet',
'style': 'background: cyan;',
}),
('r', 'red'), ]
colors = forms.ChoiceField(
label="Colors",
choices=choices,
widget=SelectWOA)
colors
字段可以在Django shell中呈现以检查结果:
(myvenv) $ ./manage.py shell
>>> from myapp.forms import *
>>> choices = ...
>>> colors = ...
>>> colors.widget.render('mycolors','')
'''<select name="mycolors">
<option value="b">blue</option>
<option value="g" disabled="disabled">green</option>
<option value="c" title="Kind of violet" style="background: cyan;">cyan</option>
<option value="r">red</option>
</select>'''
要允许多项选择,请添加以下内容:
class SelectMultipleWOA(SelectWOA, SelectMultiple):
"""
SelectMultipleWOA widget works like SelectMultiple, with options attrs.
See SelectWOA.
"""
pass
colors = forms.MultipleChoiceField(
label="Colors",
choices=choices,
widget=SelectMultipleWOA)
它将呈现<select name="mycolors" multiple>...<\select>
。
您可以使用SelectWOA和SelectMultipleWOA来扩展Select2小部件:
from django_select2.forms import Select2Mixin
class Select2MultipleWidgetWOA(Select2Mixin, SelectMultipleWOA):
"""
Select2 drop in widget for multiple select.
Works just like Select2MultipleWidget but with options attrs.
"""
pass
colors = forms.MultipleChoiceField(
label="Colors",
choices=choices,
widget=Select2MultipleWidgetWOA(
attrs={'data-placeholder': 'Any color',
'data-close-on-select': 'false',
'style': 'width:280px; height:36px;',
'title': 'Type a word to filter the menu',}
)
)
它将呈现如下内容:
'''<select name="mycolors" data-placeholder="Any color"
class="django-select2" data-minimum-input-length="0" multiple
style="width:280px; height:36px;" data-close-on-select="false"
data-allow-clear="false" title="Type a word to filter the menu">
<option value="b">blue</option>
<option value="g" disabled="disabled">green</option>
<option value="c" title="Kind of violet" style="background: cyan;">cyan</option>
<option value="r">red</option>
</select>'''
答案 5 :(得分:0)
我批准了Nihal Sharma对Django 2+的回答,它仍然很hacky,但是更漂亮:
class CustomSelect(forms.Select):
def __init__(self, attrs=None, choices=()):
self.custom_attrs = {}
super().__init__(attrs, choices)
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
option = super().create_option(name, value, label, selected, index, subindex, attrs)
# setting the attributes here for the option
if value in self.custom_attrs:
option['attrs'].update({k: v for k, v in self.custom_attrs[value].items()})
return option
class ModelChoiceFieldWithData(ModelChoiceField):
widget = CustomSelect
def __init__(self, *args, **kwargs):
self.additional_data = kwargs.pop('additional_data')
super().__init__(*args, **kwargs)
# custom method to label the option field
def label_from_instance(self, obj):
# since the object is accessible here you can set the extra attributes
self.widget.custom_attrs[obj.pk] = {f'data-{attr}': getattr(obj, attr) for attr in self.additional_data}
return super().label_from_instance(obj)
表格:
class BookingForm(forms.ModelForm):
customer = ModelChoiceFieldWithData(queryset=Customer.objects.filter(is_active=True),
additional_data=('my_field',))