问题
Here is a code that works as expected in Django 1.9:
class MultipleBooleanField(forms.MultiValueField):
def __init__(self, *args, **kwargs):
self.fieldnames = kwargs.pop('fields')
fields = [ forms.BooleanField(required=False) for x in self.fieldnames ]
super(MultipleBooleanField, self).__init__(fields=fields,
require_all_fields=False, *args, **kwargs)
self.widget = MultipleBooleanWidget(widgets=[ f.widget for f in fields ])
self.widget.fieldnames = self.fieldnames
def compress(self, datalist):
# return a list of the fieldnames, datalist is a list of booleans
print('compress datalist:', datalist)
if self.required and not any(datalist):
raise forms.ValidationError('You must choose at least one value')
return [ self.fieldnames[i] for i in range(len(datalist)) if datalist[i] ]
class MultipleBooleanWidget(forms.MultiWidget):
def render(self, name, value, attrs=None, renderer=None):
if not value:
value = [ False for x in self.fieldnames ]
rendered_widgets = [ x.render(name, value[i]) for i,x in enumerate(self.widgets) ]
items = [ '%s %s' % (rendered_widgets[i], f)
for (i,f) in enumerate(self.fieldnames) ]
return ' '.join(items)
def decompress(self, value):
# return a list of booleans, value is a list of fieldnames
print('decompress value:', value)
if not value:
return [ False for x in self.fieldnames ]
return [ x in value for x in self.fieldnames ]
With Django 1.11, it no more works, the ValidationError
is always raised. The datalist
is always a list containing only False
. The decompress
method is never called.
I tried to implement a value_from_datadict
method as suggested in very old posts, but no success.
I take a look to Djando code and it seems that Django does not like the result of the field (the return value of compress
) to be a list, so I tried to transform it into a string (as comma joined values). But the behaviour remains the same.
Any ideas?
EDIT:
Looking at the HTML source, it appears that subwidgets are not rendered correctly: they all have the same name, and have no id. Suppose the field name is Valeurs
:
In Django 1.9, the HTML is:
<tr><th><label for="id_Valeurs_0">Valeurs :</label></th><td><input checked="checked" id="id_Valeurs_0" name="Valeurs_0" type="checkbox" /> Part du total com <input checked="checked" id="id_Valeurs_1" name="Valeurs_1" type="checkbox" /> Part du total Qté <input checked="checked" id="id_Valeurs_2" name="Valeurs_2" type="checkbox" /> ...
In Django 1.11, the HTML is:
<tr><th><label for="id_Valeurs_0">Valeurs :</label></th><td><input type="checkbox" name="Valeurs" checked /> Part du total com <input type="checkbox" name="Valeurs" checked /> Part du total Qté <input type="checkbox" name="Valeurs" checked />
I have others MultiValueField/MultiWidget
that work correctly, written very similarly. I really don't understand where is the problem.
回答1:
Here is what the release notes say
The Widget.format_output() method is removed. Use a custom widget template instead.
https://docs.djangoproject.com/en/1.11/releases/1.11/#changes-due-to-the-introduction-of-template-based-widget-rendering
回答2:
I found the problem: it comes from the calculation of rendered_widgets
in the render
method, it must be:
rendered_widgets = [ x.render('%s_%d' % (name,i), value[i]) for i,x in enumerate(self.widgets) ]
With that solution, the rendered HTML has correct names, but still have no id. But it works.
I don't understand why django team removed the format_output
method of MultiWidget: it was usefull, easy, high level. Having to deal with render
is painful, but perhaps I missed something...
来源:https://stackoverflow.com/questions/46452755/migrating-django-from-1-9-to-1-11-trouble-with-multivaluefield-and-multiwidget