Django MultiValueField - How to pass choices to ChoiceField? Django MultiValueField - How to pass choices to ChoiceField? django django

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.