Django multiwidget and validation issue

723 views Asked by At

I have been subclassing multiwidget to create an HTML5 range slider synchronised with a NumberInput field using this code:

class SplitRangeNumberWidget(MultiWidget):
    def __init__(self):
        widgets = (
                 forms.NumberInput(attrs={'type':'range',
                                             'onchange':'this.nextElementSibling.value=this.value',
                                             'oninput':'this.nextElementSibling.value=this.value',
                                             'step':'any',
                                             'min':0,
                                             'max':1}),

                   forms.NumberInput(attrs={'step':'any',
                                            'onchange':'this.previousElementSibling.value=this.value',
                                            'oninput':'this.previousElementSibling.value=this.value',})
                   )
        super(SplitRangeNumberWidget, self).__init__(widgets)

    def decompress(self, value):
        if value:
            return [value, value]
        return [None, None]

When I instanciate it and use it in a Form such as:

class ParameterForm(forms.ModelForm):
    class Meta:
        model = Parameter
        fields = ['name','value','min_value','max_value']
        widgets = {'value':SplitRangeNumberWidget()}

, the widget works fine: changing the slider or the numberinput does change the other field. However, when doing a POST, the form does not validate and I get this error in form.errors (for 3 parameters):

[{'value': ['Enter a number.']}, {'value': ['Enter a number.']}, {'value': ['Enter a number.']}]

The widgets alone do work well and form is correctly bound. But not in a multiwidget. What am I doing wrong? I added

def value_from_datadict(self, data, files, name):
    num = [widget.value_from_datadict(data, files, name + '_%s' % i)
        for i, widget in enumerate(self.widgets)]
    return [float(num[0]), float(num[1])]

but this still does not work.

Thanks for your help.

1

There are 1 answers

1
Zebulon On

I found the solution: I needed to implement

def value_from_datadict(self, data, files, name):
        value_list = [widget.value_from_datadict(data, files, name + '_%s' % i)
                      for i, widget in enumerate(self.widgets)]    
        try:
            value = value_list[0]
        except ValueError:
            return None
        else:
            return value

To paraphrase the documentation: The default implementation of value_from_datadict() returns a list of values corresponding to each Widget. This is appropriate when using a MultiWidget with a MultiValueField, but since we want to use this widget with a TextField which takes a single value, we have overridden this method to combine the data of all the subwidgets into a value.