Django、インラインフォームセットでcommit=Falseを使う

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

Python - Django
2018年11月22日21:14に更新(約21日前)
2018年10月18日11:42に作成(約56日前)

旧ブログ移行記事です。

概要

以下のようなモデルがあったとしましょう。

class File(models.Model):
    name = models.CharField('ファイル名', max_length=255)
    src = models.FileField('添付ファイル')
    target = models.ForeignKey(
        Post, verbose_name='紐づく記事',
        blank=True, null=True,
        on_delete=models.SET_NULL
    )
    user = models.ForeignKey(
        User, verbose_name='ユーザー',
        blank=True, null=True,
        on_delete=models.SET_NULL
    )

Django、インラインフォームセットを使う とほぼ同様なのですが、userというフィールドが増えています。このuserには、ログインユーザーを紐づけたいとします。

普通にインラインフォームセットを使うと、ユーザーのselect要素も表示されてしまいます。

ログインユーザーを自動で紐づけたいので、当然表示すべきではありません。 この手の処理は通常のモデルフォームでもよくあり、一般的なイディオムです。以下のように実装します。

file = form.save(commit=False)
file.user = request.user
file.save()

commit=Falsesaveメソッドを呼び出し、データベースに保存する前のモデルインスタンスを取得します。その後は自由に属性の設定ができるので、file.user = request.userとし、そしてsaveメソッドを呼び出し実際に保存します。

これをフォームセットでどう行うか、が今回の主題です。 Django、インラインフォームセットを使うのコードを基に変更します。

forms.py

まずですが、ユーザーの選択欄をhtmlとして表示したくないので、excludeに指定しましょう。

FileFormset = forms.inlineformset_factory(
    Post, File, exclude=('user',),
    extra=5, max_num=5
)

views.py

formset.save()の部分を以下の処理に変更します。

# instancesは、新たに作成されたfileと更新されたfileが入ったリスト
instances = formset.save(commit=False)

# 削除チェックがついたfileを取り出して削除
for file in formset.deleted_objects:
    file.delete()

# 新たに作成されたfileと更新されたfileを取り出し、ユーザーを紐づけて保存
for file in instances:
    file.user = request.user
    file.save()

フォームセットにもsaveメソッドがあり、commit=Falseという引数も存在します。呼び出すと、データベースへ保存する前のモデルインスタンスのリストが取得できます。

instances = formset.save(commit=False)

フォームセットのsaveメソッドをcommit=Falseで呼び出した場合の注意点が2つあります。 まず1つが、削除にチェックを入れたデータがあった場合、その削除を手動で行う必要があります。

for file in formset.deleted_objects:
    file.delete()

もう一つが、保存処理も手動で行う必要があります。

for file in instances:
    file.user = request.user
    file.save()

特に削除を手動で行うのは忘れがちなので、注意しましょう。また、通常のモデルフォームにも言えることですが、ManyToManyFieldが含まれている場合はformset.save_m2m()を呼び出す必要があります。

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

記事にコメントする