将列表存储为逗号分隔字符串的 Django 字段

Com*_*fox 8 python django

我正在寻找一种将 Python 列表作为逗号分隔字符串存储在数据库中的解决方案。在做了一些研究并得出结论认为 Django 没有像这样开箱即用的东西后,我决定自己写这个。

这是我想出的模型字段:

class CommaSeparatedStringsField(models.CharField):
    def from_db_value(self, value, *args):
        if not value:
            return []

        return value.split(',')

    def to_python(self, value):
        if isinstance(value, list):
            return value

        return self.from_db_value(value)

    def get_prep_value(self, value):
        return ','.join(value)

    def value_to_string(self, obj):
        return self.get_prep_value(self.value_from_object(obj))

    def formfield(self, **kwargs):
        defaults = {'form_class': ListField}
        defaults.update(kwargs)

        return models.Field.formfield(self, **defaults)
Run Code Online (Sandbox Code Playgroud)

非常简单的东西,到目前为止,没有问题。

与此模型字段关联的表单字段和小部件应该是一个多选框。列表中的每个元素都是选择字段中的一个选项。

如果您想知道我为什么要这样,它是用于Bootstrap Tags Input 的,这是一个用于输入“标签”(多个值)的输入小部件。这可以使用文本框工作,但我更愿意在 Python 中将此字段表示为列表。

现在有了 MultipleChoiceField(它使用 MultipleSelect 小部件),但它有一个预定义的选项列表。这不是我想要的,在我的情况下,选项数据,我不关心选定的选项。

所以我写了一个自定义表单字段和小部件,它继承了上述类:

class ListWidget(SelectMultiple):
    def render_options(self, options):
        output = []

        for option_value, option_label in options:
            output.append(self.render_option([], option_value, option_label))

        return '\n'.join(output)


class ListField(MultipleChoiceField):
    widget = ListWidget

    def prepare_value(self, value):
        return [(x, x) for x in value]
Run Code Online (Sandbox Code Playgroud)

我覆盖了 SelectMultiple 的 render_options() 以将数据呈现为选项而不是预定义的选择,并且我覆盖了 MultipleChoiceField 的验证以删除选择检查和 prepare_value 以将列表转换为元组列表(option_value 和 option_label)。

这在大多数情况下有效。问题是它在提交为空时似乎没有保存该字段,我不明白为什么。当我使用 MultipleChoiceField 本身而不是我编写的自定义表单字段和小部件时,也会发生这种情况。

我试图使用调试器缩小范围,但我找不到这里到底发生了什么。我可以看到表单的清理数据具有正确的值([],空列表),但是当该字段被保存时,它具有旧值。

我在这里俯瞰什么?