Django、フォームセットでのcommit=Falseの注意点

2018-10-18 / PythonDjango

概要

Djangoでフォームセットを使うシリーズの1つです。通常のモデルフォームと同様に、モデルフォームセット、インラインフォームセットはcommit=Falseで保存ができますが、気を付ける点がいくつかあります。

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

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には、ログインユーザーを紐づけたいとします。

この手の処理は通常のモデルフォームでもよくあり、一般的なイディオムです。次のように実装します。

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

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

これをフォームセットでどう行うか、が今回の主題ですが、幾つかの注意点があります。Django、インラインフォームセットの基本的な使い方のコードを基に変更します。

ユーザーの選択欄を非表示にする

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

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

フォームセットでのcommit=False

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()を呼び出す必要があります。

この記事の関連記事

Djangoでフォームセットを使うシリーズ

2018-10-17 / PythonDjangoシリーズ・まとめ

- Djangoにはフォームセットという、複数のフォームを一括で扱うための機能があります。いくつか種類があり、様々な機能があるので、それらを紹介していきます。

Django、インラインフォームセットの基本的な使い方

2018-10-18 / PythonDjango

- Djangoでフォームセットを使うシリーズの1つです。今回はインラインフォームセットについてです。記事に添付するファイルがあったとして、記事の作成時に一緒にファイルを複数作ることができるようになります。

コメント欄

記事にコメントする

まだコメントはありません。