Django MultiValueField - How to pass choices to ChoiceField?
I ran into this exact same problem and solved it like this:
class InputAndChoiceWidget(widgets.MultiWidget): def __init__(self,*args,**kwargs): myChoices = kwargs.pop("choices") widgets = ( widgets.TextInput(), widgets.Select(choices=myChoices) ) super(InputAndChoiceWidget, self).__init__(widgets,*args,**kwargs)class InputAndChoiceField(forms.MultiValueField): widget = InputAndChoiceWidget def __init__(self,*args,**kwargs): # you could also use some fn to return the choices; # the point is, they get set dynamically myChoices = kwargs.pop("choices",[("default","default choice")]) fields = ( fields.CharField(), fields.ChoiceField(choices=myChoices), ) super(InputAndChoiceField,self).__init__(fields,*args,**kwargs) # here's where the choices get set: self.widget = InputAndChoiceWidget(choices=myChoices)
Add a "choices" kwarg to the widget's constructor. Then explicitly call the constructor after the field is created.
ModelChoiceField
is technically a ChoiceField
, but it doesn't actually use any of the ChoiceField
's implementations. So, here's how I use it.
class ChoiceInputMultiWidget(MultiWidget): """Kindly provide the choices dynamically""" def __init__(self, attrs=None): _widget = ( Select(attrs=attrs), TextInput(attrs=attrs) ) super().__init__(_widget, attrs)class ModelChoiceInputField(MultiValueField): widget = ChoiceInputMultiWidget def __init__(self, *args, **kwargs): _fields = ( ModelChoiceField(queryset=Type.objects.all()), CharField() ) super().__init__(_fields, *args, **kwargs) # Use the auto-generated widget.choices by the ModelChoiceField self.widget.widgets[0].choices = self.fields[0].widget.choices
Have a look at the source of __init__
of forms.MultiValueField
:
def __init__(self, fields=(), *args, **kwargs): super(MultiValueField, self).__init__(*args, **kwargs) # Set 'required' to False on the individual fields, because the # required validation will be handled by MultiValueField, not by those # individual fields. for f in fields: f.required = False self.fields = fields
So I would overwrite the __init__
probably like this:
def __init__(self, *args, **kwargs): choices = kwargs.pop("choices",[]) super(InputAndChoiceField, self).__init__(*args, **kwargs) self.fields = ( fields.CharField(), fields.ChoiceField(choices=choices), )
You might even want to do super(MultiValueField, self).__init__(*args, **kwargs)
instead of super(InputAndChoiceField, self).__init__(*args, **kwargs)
because you are setting the fields yourself instead of getting them via parameters.