将Django从1.9迁移到1.11:MultiValueField和MultiWidget行为出现问题

时间:2017-09-27 16:20:24

标签: python django django-forms

这是一个在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 ]

使用Django 1.11,它不再有用,ValidationError总是被提升。 datalist始终是仅包含False的列表。 永远不会调用decompress方法

我尝试按照旧帖子中的建议实施value_from_datadict方法,但没有成功。

我看看Djando代码,似乎Django不喜欢字段的结果(compress的返回值)作为列表,所以我试图将它转换为字符串(如逗号加入了值)。但行为仍然相同。

有什么想法吗?

修改 查看HTML源代码,看起来子窗口小部件未正确呈现:它们都具有相同的名称,并且没有id。假设字段名称为Valeurs

在Django 1.9中,HTML是:

<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" /> ...

在Django 1.11中,HTML是:

<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 /> 

我让其他MultiValueField/MultiWidget工作正常,写得非常相似。我真的不明白问题出在哪里。

2 个答案:

答案 0 :(得分:2)

以下是发行说明所说的内容

  

删除了Widget.format_output()方法。改为使用自定义小部件模板。

https://docs.djangoproject.com/en/1.11/releases/1.11/#changes-due-to-the-introduction-of-template-based-widget-rendering

答案 1 :(得分:1)

我发现了问题:它来自rendered_widgets方法中render的计算,必须是:

rendered_widgets = [ x.render('%s_%d' % (name,i), value[i]) for i,x in enumerate(self.widgets) ]

使用该解决方案,呈现的HTML具有正确的名称,但仍然没有id。但它确实有效。

我不明白为什么django团队删除了MultiWidget的format_output方法:它很有用,简单,高级。必须处理render是痛苦的,但也许我错过了一些东西......