我必须将title属性添加到ModelChoiceField的选项中。这是我的管理员代码:
class LocModelForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
super(LocModelForm,self).__init__(*args,**kwargs)
self.fields['icons'] = forms.ModelChoiceField(queryset = Photo.objects.filter(galleries__title_slug = "markers"))
self.fields['icons'].widget.attrs['class'] = 'mydds'
class Meta:
model = Loc
widgets = {
'icons' : forms.Select(attrs={'id':'mydds'}),
}
class Media:
css = {
"all":("/media/css/dd.css",)
}
js=(
'/media/js/dd.js',
)
class LocAdmin(admin.ModelAdmin):
form = LocModelForm
我可以为select小部件添加任何属性,但我不知道如何向选项标签添加属性。任何的想法 ?
答案 0 :(得分:18)
首先,不要修改__init__
中的字段,如果要覆盖小部件使用Meta
内部类,如果要覆盖表单字段,请按正常方式声明它们(非{ -model)表格。
如果Select
窗口小部件无法执行您想要的操作,那么只需创建自己的窗口小部件即可。原始窗口小部件使用render_option
方法获取单个选项的HTML表示形式 - 创建子类,覆盖它,并添加您想要的任何内容。
class MySelect(forms.Select):
def render_option(self, selected_choices, option_value, option_label):
# look at the original for something to start with
return u'<option whatever>...</option>'
class LocModelForm(forms.ModelForm):
icons = forms.ModelChoiceField(
queryset = Photo.objects.filter(galleries__title_slug = "markers"),
widget = MySelect(attrs = {'id': 'mydds'})
)
class Meta:
# ...
# note that if you override the entire field, you don't have to override
# the widget here
class Media:
# ...
答案 1 :(得分:5)
我遇到了类似的问题,我需要动态地为每个选项添加自定义属性。但是在Django 2.0中,html渲染被移动到Widget基类中,因此修改render_option
不再有效。以下是适用于我的解决方案:
from django import forms
class CustomSelect(forms.Select):
def __init__(self, *args, **kwargs):
self.src = kwargs.pop('src', {})
super().__init__(*args, **kwargs)
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
options = super(CustomSelect, self).create_option(name, value, label, selected, index, subindex=None, attrs=None)
for k, v in self.src.items():
options['attrs'][k] = v[options['value']]
return options
class CustomForm(forms.Form):
def __init__(self, *args, **kwargs):
src = kwargs.pop('src', {})
choices = kwargs.pop('choices', ())
super().__init__(*args, **kwargs)
if choices:
self.fields['custom_field'].widget = CustomSelect(attrs={'class': 'some-class'}, src=src, choices=choices)
custom_field = forms.CharField(max_length=100)
然后在视图中,使用{'form': CustomForm(choices=choices, src=src)}
呈现上下文,其中src
是这样的字典:{'attr-name': {'option_value': 'attr_value'}}
。
答案 2 :(得分:4)
这是我创建的一个继承自forms的类。选择(感谢Cat Plus Plus让我开始这个)。在初始化时,提供 option_title_field 参数,指示用于<option>
title属性的字段。
from django import forms
from django.utils.html import escape
class SelectWithTitle(forms.Select):
def __init__(self, attrs=None, choices=(), option_title_field=''):
self.option_title_field = option_title_field
super(SelectWithTitle, self).__init__(attrs, choices)
def render_option(self, selected_choices, option_value, option_label, option_title=''):
print option_title
option_value = forms.util.force_unicode(option_value)
if option_value in selected_choices:
selected_html = u' selected="selected"'
if not self.allow_multiple_selected:
# Only allow for a single selection.
selected_choices.remove(option_value)
else:
selected_html = ''
return u'<option title="%s" value="%s"%s>%s</option>' % (
escape(option_title), escape(option_value), selected_html,
forms.util.conditional_escape(forms.util.force_unicode(option_label)))
def render_options(self, choices, selected_choices):
# Normalize to strings.
selected_choices = set(forms.util.force_unicode(v) for v in selected_choices)
choices = [(c[0], c[1], '') for c in choices]
more_choices = [(c[0], c[1]) for c in self.choices]
try:
option_title_list = [val_list[0] for val_list in self.choices.queryset.values_list(self.option_title_field)]
if len(more_choices) > len(option_title_list):
option_title_list = [''] + option_title_list # pad for empty label field
more_choices = [(c[0], c[1], option_title_list[more_choices.index(c)]) for c in more_choices]
except:
more_choices = [(c[0], c[1], '') for c in more_choices] # couldn't get title values
output = []
for option_value, option_label, option_title in chain(more_choices, choices):
if isinstance(option_label, (list, tuple)):
output.append(u'<optgroup label="%s">' % escape(forms.util.force_unicode(option_value)))
for option in option_label:
output.append(self.render_option(selected_choices, *option, **dict(option_title=option_title)))
output.append(u'</optgroup>')
else: # option_label is just a string
output.append(self.render_option(selected_choices, option_value, option_label, option_title))
return u'\n'.join(output)
class LocModelForm(forms.ModelForm):
icons = forms.ModelChoiceField(
queryset = Photo.objects.filter(galleries__title_slug = "markers"),
widget = SelectWithTitle(option_title_field='FIELD_NAME_HERE')
)
答案 3 :(得分:0)
从django 1.11及更高版本中删除了render_option
方法。看到此链接:https://docs.djangoproject.com/en/1.11/releases/1.11/#changes-due-to-the-introduction-of-template-based-widget-rendering
在这里,与Kayoz的解决方案不同。我没有像示例中那样修改名称,但我希望它仍然清楚。在模型表单中,我覆盖了该字段:
class MyForm(forms.ModelForm):
project = ProjectModelChoiceField(label=_('Project'), widget=ProjectSelect())
然后我从上面声明类,并另外声明一个迭代器:
class ProjectModelChoiceIterator(django.forms.models.ModelChoiceIterator):
def choice(self, obj):
# return (self.field.prepare_value(obj), self.field.label_from_instance(obj)) #it used to be like this, but we need the extra context from the object not just the label.
return (self.field.prepare_value(obj), obj)
class ProjectModelChoiceField(django.forms.models.ModelChoiceField):
def _get_choices(self):
if hasattr(self, '_choices'):
return self._choices
return ProjectModelChoiceIterator(self)
class ProjectSelect(django.forms.Select):
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
context = super(ProjectSelect, self).create_option(name, value, label, selected, index, subindex=None, attrs=None)
context['attrs']['extra-attribute'] = label.extra_attribute #label is now an object, not just a string.
return context
答案 4 :(得分:0)
使用Django 1.11,我发现了使用已记录的API的另一种方法。如果您覆盖get_context
并深入研究结构,您将在context['widget']['optgroups'][1][option_idx]['attrs']
中看到各个选项属性。例如,在我的子类中,我有以下代码:
class SelectWithData(widgets.Select):
option_data = {}
def __init__(self, attrs=None, choices=(), option_data={}):
super(SelectWithData, self).__init__(attrs, choices)
self.option_data = option_data
def get_context(self, name, value, attrs):
context = super(SelectWithData, self).get_context(name, value, attrs)
for optgroup in context['widget'].get('optgroups', []):
for option in optgroup[1]:
for k, v in six.iteritems(self.option_data.get(option['value'], {})):
option['attrs']['data-' + escape(k)] = escape(v)
return context