【Django】1度のフォーム送信で複数テーブルにデータを保存する方法

　Djangoを使用して漫画の評価を登録できるアプリを作成することにしました。
　その際、1度のフォーム送信で複数テーブルにデータを保存する必要があったため、以下の記事を参考にしてその実装を行いました。

[Django、インラインフォームセットの基本的な使い方](https://blog.narito.ninja/detail/32/)

　では実装方法を説明します。
　説明に不要なコードは省略して記載しています。

## 使用したモデル
　なぜ1度のフォーム送信で複数テーブルにデータを保存する必要があったかというと、1つの漫画に対して複数の評価を登録できるようにしたかったからです。
　また今後評価項目を増やし、評価内容の詳細を別テーブルに保存する方法も考えています。
　まず暫定的に作成したモデルが以下のようになっています。この漫画の名前と漫画の評価を同時に保存できるようにしていきます。

models.py
```python
from django.db import models
from django.core.validators import MaxValueValidator

class Comic(models.Model):
    comic_name = models.CharField(verbose_name="漫画名",max_length=128,null=False,unique=True,primary_key=True)
    created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)

class ComicResult(models.Model):
    comic_name = models.ForeignKey(Comic,verbose_name="漫画名",max_length=128,null=False,on_delete=models.PROTECT)
    comic_score = models.PositiveSmallIntegerField(verbose_name="評点",validators=[MaxValueValidator(100)],null=False)
    comment = models.TextField(verbose_name="コメント",null=True)
    created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)
```

## inlineformset_factory()を使う
　漫画の名前を登録するテーブルと、漫画の評価を登録するテーブルの2つに、同時にデータを保存する方法は、このinlineformset_factory()を使うことで実現できました。

forms.py

```python
from django import forms
from e_comic.models import Comic,ComicResult

class NewComicForm(forms.ModelForm):
    class Meta():
        model = Comic
        fields = ('comic_name',)

ComicFormset = forms.inlineformset_factory(
    Comic, ComicResult, fields='__all__',
    extra=1, can_delete=False
)

```
　viewとhtmlはそれぞれ以下のようになっています。

views.py

```python
from django.http import HttpResponseRedirect
from django.shortcuts import render
from e_comic.forms import NewComicForm,ComicFormset

def comic_create(request):
    form = NewComicForm(request.POST or None)
    context = {'form': form}
    if request.method == 'POST' and form.is_valid():
        post = form.save(commit=False)
        formset = ComicFormset(request.POST,instance=post) 
        if formset.is_valid():
            post.save()
            formset.save()
            return HttpResponseRedirect('../')
    else:
        context['formset'] = ComicFormset()
        print('ERROR FORM INVALID')
    
    return render(request, 'comic_create.html', context)


```

comic_create.html
```django
{% block content %}
<h1>漫画を登録しよう</h1>
<div class="container">
  <div class="jumbotron">
      <form method="POST">
          {{ form.as_p }}
            {{ formset.management_form }}
            {% for result_form in formset %}
                {{ result_form.as_p }} 
            {% endfor %}

          {% csrf_token %}
          <input type="submit" class="btn btn-primary" value="登録">
      </form>
  </div>
</div>
{% endblock %}
```

## 実際の挙動

　挙動は以下のようになっています。
　データベースはMySQLを使用しており、接続方法は以下を参考にしています。
[いつもDjangoでMySQL(utf8mb4)を利用するときに行っているDjangoのDATABASE設定](https://qiita.com/shirakiya/items/71861325b2c8988979a2)


[![Image from Gyazo](https://i.gyazo.com/b789383a699763353ac567f9d849313b.gif)](https://gyazo.com/b789383a699763353ac567f9d849313b)

[![Image from Gyazo](https://i.gyazo.com/aa56cf6d7d9dcf37298055758070b4f9.gif)](https://gyazo.com/aa56cf6d7d9dcf37298055758070b4f9)