如何覆盖 Django 中的模型表单小部件?

How to override a Model Form widget in Django?

我正在尝试从我的 forms.py 文件中覆盖 Select 小部件 class 中存在的 render_option 方法。所以我在相应的模型表单class中添加了同名方法。但它不起作用(此方法无法覆盖)。我的 forms.py 文件看起来像

class CustomSelectMultiple(Select):

    allow_multiple_selected = True

    def render_option(self, selected_choices, option_value, option_label):
        print 'Inside custom render_option\n\n'
        if option_value is None:
            option_value = ''
        option_value = force_text(option_value)
        if option_value in selected_choices:
            selected_html = mark_safe(' selected="selected"')
            if not self.allow_multiple_selected:
                # Only allow for a single selection.
                selected_choices.remove(option_value)
        else:
            selected_html = ''
        return format_html('<option value="{}" data-img-src="www.foo.com" {}>{}</option>',
                           option_value,
                           selected_html,
                           force_text(option_label))

    def render_options(self, choices, selected_choices):
        print 'Inside custom render_options\n\n'
        print self
        print choices
        # Normalize to strings.
        selected_choices = set(force_text(v) for v in selected_choices)
        output = []
        for option_value, option_label in chain(self.choices, choices):
            if isinstance(option_label, (list, tuple)):
                output.append(format_html('<optgroup label="{}">', force_text(option_value)))
                for option in option_label:
                    output.append(self.render_option(selected_choices, *option))
                output.append('</optgroup>')
            else:
                output.append(self.render_option(selected_choices, option_value, option_label))
        #print output
        return '\n'.join(output)

    def render(self, name, value, attrs=None, choices=()):
        print 'Inside custom render\n\n'
        if value is None:
            value = []
        final_attrs = self.build_attrs(attrs, name=name)
        output = [format_html('<select multiple="multiple"{}>', flatatt(final_attrs))]
        options = self.render_options(choices, value)
        if options:
            output.append(options)
        output.append('</select>')
        return mark_safe('\n'.join(output))

    def value_from_datadict(self, data, files, name):
        if isinstance(data, MultiValueDict):
            return data.getlist(name)
        return data.get(name)


class GuideUpdateForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(GuideUpdateForm, self).__init__(*args, **kwargs)
        self.fields['date_modified'].widget = HiddenInput()
        self.fields['point_of_interest'].widget = CustomSelectMultiple()

    class Meta:
        fields = ('name', 'image', 'point_of_interest', 'date_modified', )
        model = Guide

我也试过改变我的 Meta class 比如,

class Meta:
        fields = ('name', 'image', 'point_of_interest', 'date_modified', )
        model = Guide
        widgets = {
            'point_of_interest': SelectMultiple(attrs={'data-img-src': 'www.foo.com'}),
        }

但它仅将属性 data-img-src 添加到 select 标签,而不是添加到 select 标签内的所有 option 标签。

注意 SelectMultiple class 调用 Select class 的 renderoptions 方法,后者进一步调用 renderoption 方法有 attrs=None 个关键字参数。

我设法通过将 db 值传递给 choices kwargs 来解决这个问题。

from models import poi
class GuideUpdateForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(GuideUpdateForm, self).__init__(*args, **kwargs)
        self.fields['date_modified'].widget = HiddenInput()
        self.fields['point_of_interest'] = forms.ChoiceField(widget=CustomSelectMultiple(), choices=[(i.id,i.name) for i in poi.objects.all()])

根据您自己的解决方案判断,您可能一直在寻找 ModelChoiceField

self.fields['point_of_interest'] = forms.ModelChoiceField(widget=CustomSelectMultiple(),
                                                          queryset=poi.objects.all())

queryset参数由"A QuerySet of model objects from which the choices for the field will be derived, and which will be used to validate the user’s selection."

组成

does it create a list of tuples of ids, names? Because I want the option tag to look like option value="id">name</option>

我很确定默认值是 id, __str__,其中 __str__ 是模型的字符串表示形式。如果您希望它特定于名称,那么您可以覆盖此字段并设置 label_from_instance

class MyModelChoiceField(ModelChoiceField):
    def label_from_instance(self, obj):
        return obj.name