複数のModelFormを一括登録する

# はじめに
おはこんばんちは。@cosumi77と申します。

本記事はdjango初心者が記載しました。
誤りがある場合などは是非ご指摘頂けるとありがたいです。

# 本記事の目的
下図のように異なるテーブルの複数のModelFormを一括で登録したい場合があるかと思います。
（同じテーブルのModelFormを複数登録する場合はModelFormSetを使えば良い）
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388176/c7b0c44a-74af-f59a-45e6-a7cdf55e4b15.png)

これを実装しようと色々調べてみたのですがヒットしなかったため、情報共有しようと思いました。
また、この実装を行う際に注意すべき事がありますので、そちらについても記載します。

# 実装

まずはモデルを作成します。
（何故かカラム名にナンバリングしてある理由は後述）

```python:models.py
from django.db import models

class test1(models.Model):
    name1 = models.CharField(max_length=10)
    title1 = models.CharField(max_length=100)
    memo1 = models.CharField(max_length=100)

class test2(models.Model):
    name2 = models.CharField(max_length=10)
    title2 = models.CharField(max_length=100)
    memo2 = models.CharField(max_length=100)
```

続いて、ModelFormを実装します。
```python:forms.py
from django import forms
from .models import test1, test2

class Test1ModelForm(forms.ModelForm):

    class Meta:
        model = test1
        fields = '__all__' 

class Test2ModelForm(forms.ModelForm):

    class Meta:
        model = test2
        fields = '__all__' 
```

Viewは書き方が怪しいですが、一応動くので載せておきます。
（もっとこうしたら良い等ありましたら、是非ともコメントください！）

複数のFormをcontextとして渡します。
```python:
from django.views.generic.edit import View
from django.template.response import TemplateResponse
from django.http.response import HttpResponseRedirect
from .forms import (
    Test1ModelForm, Test2ModelForm,
)
from django.urls import reverse

class testView(View):

    def get(self, request, *args, **kwargs):

        #複数のフォームをコンテキストとして渡す
        context = {
            'main_form' : Test1ModelForm(),
            'detail_form' : Test2ModelForm(),
        }
    
        return TemplateResponse(request, 'test.html', context)

    def post(self, request, *args, **kwargs):
        form1 = Test1ModelForm(request.POST)
        form2 = Test2ModelForm(request.POST)

        #全てのフォームのvalidationが通ったらsave
        if form1.is_valid() and form2.is_valid():
            form1.save()
            form2.save()
        
        return HttpResponseRedirect(reverse('test'))
```

templateでは各々のformを表示するように記述します。


```html:template.html
<form method="post">
    {% csrf_token %}
        <table>
            {{ main_form.as_table }}
        </table>
        <br>

        <table>
            {{ detail_form.as_table }}
        </table>

    <input type="submit" value="登録">
</form>
```

最後にpathを通します。

```python:urls.py
from django.contrib import admin
from django.urls import path

from test1.views import testView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('test/', testView.as_view(),name='test')
]
```

後はsettings.pyにテンプレートの記述を追加すれば完了です(説明は省略)。
![disp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388176/ff7cd58f-fe3e-cbfa-b127-5f6f3e584dad.png)

# 動作検証
実際にフォームに入力して、各々のテーブルに登録されるか確認してみます。
![disp3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388176/f6bdacd9-46d5-2049-f9b9-52a270642ce1.png)

登録ボタンを押下すると、きちんと登録出来てました。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388176/bedaa43d-b867-9fb1-1654-7c5b2ab314d4.png)
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388176/5592de7e-5a8d-7eb1-9a42-b7b9ca7ce33e.png)

# 実装にあたっての注意点
以下のようにカラム名が重複している場合、一番最後に定義したmodelを対象したmodelFormに入力された値で更新がかかります。
要は、同じ名前だと値が上書きされてしまうようです。

```python:models.py
from django.db import models

class test1(models.Model):
    name = models.CharField(max_length=10)
    title = models.CharField(max_length=100)
    memo = models.CharField(max_length=100)

#各カラム名が全く同じ
class test2(models.Model):
    name = models.CharField(max_length=10)
    title = models.CharField(max_length=100)
    memo = models.CharField(max_length=100)
```
具体的には、例えば以下のように入力した後に登録してみると、どちらのテーブルにもddd、eee、fffが登録されます。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388176/3a0e82ad-8cd5-01db-d27b-882230cdaafe.png)

これは以下のようにform側でlabelの文言を変えるだけでは解消しません。

```python:forms.py
from django import forms
from .models import test1, test2

class Test1ModelForm(forms.ModelForm):

    class Meta:
        model = test1
        fields = '__all__' 

class Test2ModelForm(forms.ModelForm):


    class Meta:
        model = test2
        fields = '__all__' 
        
        #★ラベルで別名を付与
        labels={
            'name':'name2',
            'title':'title2',
            'memo':'memo2',
        }
```

色々調べてみたのですが、このあたりの良い解決策は見当たりませんでした。
というわけで、model上での定義名を工夫し、カラム名が一意になるように定義する必要があるというのが私の結論です。
（良い方法がありましたら教えてくださいm(_ _)m）