I'm trying to understand why I can't specify choices to a form field's widget if I'm overriding a ModelForm's field in Django. It works if I give the choices to the field but not the widget. My understanding is/was that if you give choices to a field it'll be passed onto the widget for rendering. I know I can get this to work with any of the first three snippets below but I just wanted to fully understand why this one way doesn't work.
This is my ModelForm code, thanks!
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)
开发者_JS百科 self.fields['attending_ceremony'] = forms.TypedChoiceField(
required=True,
widget=forms.RadioSelect(choices=Guest.CHOICES)
)
I think the best way of explaining is to walk through the code for ChoiceField
, the superclass of 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)
With your example,
self.fields['attending_ceremony'] = forms.TypedChoiceField(
required=True,
widget=forms.RadioSelect(choices=Guest.CHOICES)
)
- The widget is initialised, with choices=guest.Choices
super(ChoiceField, self).__init__
sets self.widget=widget. The widget's choices are still set.self.choices=choices
sets the choices for the field and the widget to the default()
, because it wasn't specified (see_set_choices
above).
Hope that makes sense. Looking at the code also explains why your other examples work. Either the choices are set for the widget and the choice field at the same time, or widget's choices are set after the choice field has been initialised.
精彩评论