【Django】ModelFormSetでフォームの初期値を設定する方法

## はじめに
ModelFormSetに初期値を設定する方法を説明します。

ModelFormは1つのテンプレートに1つしか作成することができません。
複数のModelFormを同時に作成する場合は、ModelFormSetを使用します。

## 概要
ModelFormSetに初期値を設定する方法は様々ありますが
こちらでは、ModelFormSetのインスタンス化時に初期値を設定する方法を説明します。
ビューからフォームに値を渡す方法も説明しているため、動的に初期値を設定することも可能です。
それでは、さっそく実装していきましょう。

## 説明
#### モデル
以下のモデル、フォーム、ビューを例に説明します。

```python:apps/memo/models.py
from django.db import models

class Memo(models.Model):
    title = models.CharField('タイトル', max_length=20)
    text = models.TextField('内容')

    def __str__(self):
        return self.title
```

#### ビュー
画面にModelFormSetを表示するために、contextにインスタンスを渡します。
```python:apps/memo/views.py
from django.views.generic import TemplateView
from apps.memo.models import Memo


class MemoView(TemplateView):
    model = Memo
    template_name = "memo/template.html"

    def get_context_data(self, *args, **kwargs, request)
        context = super().get_context_data(self, *args, **kwargs, request)
        context['formset'] = MemoFormSet()

        return context
```

#### フォーム
BaseMemoFormSetのinit関数で、初期値を設定できます。
self.querysetに代入された値が初期値となります。
そのため、Noneを代入した場合は初期値なしということになります。
self.querysetの値を上書きしない場合は、modelformset_factoryの第一引数で指定したモデルのすべてのデータが初期値に設定されます。ご注意ください。
```python:apps/memo/forms.py
from django.forms import ModelForm, BaseModelFormSet, modelformset_factory
from apps.memo.models import Memo

class MemoForm(ModelForm):
    class Meta:
        model = Memo
        fields = ('title', 'text')


class BaseMemoFormSet(BaseModelFormSet):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 初期値を設定する
        self.queryset = None

MemoFormSet = modelformset_factory(Memo, Form=MemoForm, formset=BaseMemoFormSet)

```



### 初期値を設定する
①②の順番でModelFormSetに初期値を設定します。
①ビューからフォームに値を渡す
②フォームで値を受け取り、初期値を設定する

ログインユーザーに紐づくデータを初期値に持たせたい場合などに活用できます。
ビューからユーザーIDを渡して、フォームでユーザーIDに紐づくデータを初期値に設定する。
といったことが可能です。

#### ビュー
MemoFormSetインスタンス化時に、MemoViewからMemoFormSetに値を渡します。

```python:apps/memo/views.py
from django.views.generic import TemplateView
from apps.memo.models import Memo
from apps.memo.forms import MemoFormSet


class MemoView(TemplateView):
    model = Memo
    template_name = "memo/template.html"

    def get_context_data(self, *args, **kwargs, request)
        context = super().get_context_data(self, *args, **kwargs, request)
        # MemoFormSetに値を渡す(key:'title', value:'test')
        context['formset'] = MemoFormSet('title'='test') 

        return context
```

#### フォーム
BaseMemoFormSetのinit関数内でMemoViewで渡された値を受け取ることができます。
以下のソースではMemoViewから渡された値を、初期値の取得の条件に使用しています。

```python:apps/memo/forms.py
from django.forms import ModelForm, BaseModelFormSet, modelformset_factory
from apps.memo.models import Memo

class MemoForm(ModelForm):
    class Meta:
        model = Memo
        fields = ('title', 'text')


class BaseMemoFormSet(BaseModelFormSet):
    def __init__(self, *args, **kwargs):
        # MemoViewで渡した'test'を受け取る
        title = kwargs.pop('title')
        super().__init__(*args, **kwargs)
        # 初期値を設定する
        self.queryset = Memo.objects.filter(title__startswith=title)


MemoFormSet = modelformset_factory(Memo, Form=MemoForm, formset=BaseMemoFormSet)

```

## 参考

[Django 公式サイト](https://docs.djangoproject.com/en/4.1/topics/forms/modelforms/#django.forms.models.BaseModelFormSet)