Изменение модели после полевой валидации
Я внедряю систему тегов. В настоящее время модели выглядят следующим образом:
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
на каждой из валидированных строк тегов.
Существует ли метод "hook" в формах, который позволит мне сделать это чисто? Я прочитал всю документацию и 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
Соединяет очищенные теги, а затем возвращает PK тега TagManager
, к которому они были добавлены.