为什么在ModelForm.__init__中指定Widget的choices无效

2 投票
1 回答
1207 浏览
提问于 2025-04-16 02:28

我想弄明白为什么在Django中,如果我重写一个ModelForm的字段,就不能给表单字段的小部件指定选项。如果我直接给字段指定选项是可以的,但给小部件指定就不行。我原本以为如果给字段指定了选项,这些选项会传递给小部件进行渲染。我知道在下面的前三个代码片段中我可以让它工作,但我只是想完全理解为什么这种方式不行。

这是我的ModelForm代码,谢谢!

from django import forms
from models import Guest

    class RSVPForm(forms.ModelForm):

        class Meta:
            model = Guest

        def __init__(self, *args, **kwargs):

            """
            Override a form's field to change the widget
            """

            super(RSVPForm, self).__init__(*args, **kwargs)

                # This works
                self.fields['attending_ceremony'].required = True
                self.fields['attending_ceremony'].widget=forms.RadioSelect(choices=Guest.CHOICES)

                # This works
                self.fields['attending_ceremony'].required = True
                self.fields['attending_ceremony'].widget=forms.RadioSelect()
                self.fields['attending_ceremony'].choices=Guest.CHOICES

                # This works
                self.fields['attending_ceremony'] = forms.TypedChoiceField(
                    required=True,
                    widget=forms.RadioSelect,
                    choices=Guest.CHOICES
                )

                # This doesn't - the choices aren't set (it's an empty list)
                self.fields['attending_ceremony'] = forms.TypedChoiceField(
                    required=True,
                    widget=forms.RadioSelect(choices=Guest.CHOICES)
                )

1 个回答

2

我觉得最好的解释方式是通过查看这段代码,它是ChoiceField的代码,而TypeChoiceField是它的子类。

class ChoiceField(Field):
    widget = Select
    default_error_messages = {
        'invalid_choice': _(u'Select a valid choice. %(value)s is not one of the available choices.'),
    }

    def __init__(self, choices=(), required=True, widget=None, label=None,
                 initial=None, help_text=None, *args, **kwargs):
        super(ChoiceField, self).__init__(required=required, widget=widget, label=label,
                                        initial=initial, help_text=help_text, *args, **kwargs)
        self.choices = choices

    def _get_choices(self):
        return self._choices

    def _set_choices(self, value):
        # Setting choices also sets the choices on the widget.
        # choices can be any iterable, but we call list() on it because
        # it will be consumed more than once.
        self._choices = self.widget.choices = list(value)

    choices = property(_get_choices, _set_choices)

根据你的例子,

     self.fields['attending_ceremony'] = forms.TypedChoiceField(
                required=True,
                widget=forms.RadioSelect(choices=Guest.CHOICES)
            )
  1. 首先,初始化了一个小部件(widget),并设置了选项为guest.Choices。
  2. super(ChoiceField, self).__init__这行代码把小部件(widget)设置为当前的widget。小部件的选项依然被保留。
  3. self.choices=choices这行代码把字段的选项和小部件的选项都设置为默认值(),因为没有特别指定(可以参考上面的_set_choices)。

希望这样解释能让你明白。查看这段代码也能解释为什么你其他的例子能正常工作。要么是在初始化的时候同时为小部件和选择字段设置选项,要么是在选择字段初始化后再为小部件设置选项。

撰写回答