前置き
恐らくこの記事を見ている方は僕と同じ「ManyToManyFieldを直接formで使いたい」と思っている方でしょう。
そんな方に向けてどう対処するかを自分なりにまとめてみました。
ManyToManyFieldを直接formで使うとどうなる?
その前に、まずはモデル構造から見ていきましょう。
今回は記事とタグの多対多での関係になります。
models.py
タグモデル
class Tag(models.Model):
slug = models.CharField(verbose_name='SLUG', unique=True, max_length=20, primary_key=True)
name = models.CharField(verbose_name='タグ名', unique=True, max_length=20)
created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)
記事モデル
class Article(models.Model):
title = models.CharField(verbose_name='タイトル', default='タイトルです。', max_length=30, null=False, blank=False)
text = models.TextField(verbose_name='テキスト', default='テキストです。', max_length=255, null=False, blank=False)
author = models.ForeignKey(get_user_model(), verbose_name='作成者', on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag, verbose_name='タグ', related_name='articles')
created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)
記事モデルとタグモデルは多対多の関係なので、中間テーブルを作成する必要があります。
ですが、DjangoではManyToManyFieldを使うと自動で中間テーブルを作成してくれます。なんと便利なんでしょうか。
で、そのままManyToManyFieldを使ってみました。
forms.py
class ArticleNewForm(forms.ModelForm):
class Meta:
model = Article
fields = {
'title',
'text',
'tags, #tagsがManyToManyFieldに設定したもの
}
こうするとどうなるかと言うと、
ManyToManyFieldを定義したとき、それをそのまま継承してModelFormを作成するとModelChoiceField
が設定されます
なので、formのエラー(errors)を吐かせるとこんな感じで怒られます。
正しく選択してください。 〇〇は候補にありません。
じゃあどうするかって言うと、もうやけくそになってCharFieldを作ってしまえ!!って感じですね。笑
CharFieldの作成
先ほどのforms.pyにforms.CharFieldを作成していきます。
修正したforms.py
class ArticleNewForm(forms.ModelForm):
# 追加
tags = forms.CharField(label="タグ", max_length=〇〇)
class Meta:
model = Article
fields = {
'title',
'text',
'tags, #こいつは消す
}
こうすることでcleaned_data['tags']
で入力した値を取得できます。
あとはいつも通りに使えば解決ですね。
まとめ(タグの場合)
・ManyToManyFieldをそのままModelFormのfieldsに書き込むとModelChoiceField
が設定される
・forms.CharFieldにて新たにtagsを定義し、feildsの中のtagsを消す
・forms.CharFieldのデータをcleaned_data['tags']で取得する
参考記事