我正在寻找一种解决方案,将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)
非常简单的东西,到目前为止,没有问题。
与此模型字段关联的表单字段和窗口小部件应为多选框。列表中的每个元素都是选择字段中的一个选项。
如果你想知道我为什么这么想,那就是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]
我覆盖SelectMultiple的render_options()以将数据呈现为选项而不是预定义的选项,并且我覆盖了MultipleChoiceField的验证以删除选项check和prepare_value以将列表转换为列表元组(option_value和option_label)。
这在大多数情况下都有效。问题是当它被提交为空时它似乎没有保存字段,我不明白为什么。当我使用MultipleChoiceField本身而不是我写的自定义表单字段和小部件时,也会发生这种情况。
我尝试使用调试器缩小范围,但我无法找到这里发生的事情。我可以看到表单的已清理数据具有正确的值([]
,空列表),但是当保存该字段时,它具有旧值。
我在这里俯瞰什么?