我正在实现一个标签系统。目前,模型是这样的:
class Tag(models.Model):
label = models.CharField(max_length=MAX_TAG_LENGTH)
class TagManager(models.Model):
tags = models.ManyToManyField(Tag, related_name="referrers")
def associate_tag(self, tag_label: str):
. . .
我有一个自定义字段,它将逗号截断输入,因此用户可以将标签作为逗号分隔的列表输入:
class TagsField(forms.CharField):
def to_python(self, value):
if not value:
return []
return [tag.strip() for tag in value.split(',')]
最后,我有了使用它们的模型和表单:
class Thing(models.Model):
tags = models.OneToOneField(TagManager, on_delete=models.SET_NULL, null=True)
class ThingForm(forms.ModelForm):
tags = TagsField(widget=forms.TextInput(attrs={"placeholder": "Tags", "required": False}))
class Meta:
model = Thing
fields = ["tags"]
<标题>我的问题是,如果我填充和验证表单:
form = ThingForm(data={"tags": ["One", "Two"]})
form.is_valid()
我得到错误:
{'tags': ["“["One", "Two"]” value must be an integer."]}
我猜这是因为它试图将字符串化列表放在OneToOneField
中,这是行不通的。
我真正需要做的是在验证字段之后,我需要迭代to_python
的结果,并在每个验证的标记字符串上调用thing_instance.tags.associate_tag
。
有"钩子"吗?方法能让我干净利落地完成这个吗?我已经通读了文档和Form
源代码,但找不到任何明显的候选。
当我写这篇文章时,我意识到这是我需要的clean_*
方法。我并不觉得这是"清洁"。
解决方案是增加一个clean_tags
方法ThingForm
类:
def clean_tags(self):
tags = self.cleaned_data["tags"]
for tag in tags:
self.instance.tags.associate_tag(tag)
return self.instance.tags.pk
它关联清洗过的标签,然后返回它们被添加到的TagManager
的PK。