1対多のアソシエーション実装＆確認ダイアログ

# 前回
####[1. Djangoを使ったCRUD機能実装＆Herokuデプロイ](https://qiita.com/K_Kei/items/78b566cea4fe8a14095c)

## アソシエーション実装（1対多）
以前に作成したブログに対して、コメントを投稿する機能を追加していきます。

### モデルの作成

新たに`Comment`というモデルを追加します。
この時、ブログとアソシエーションを組むために`ForeignKey`を使って以下のように記述します。

[blog_app/models.py]

```py
from django.db import models


class Blog(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField(max_length=300)


# 以下を追加
class Comment(models.Model):
    content = models.TextField(max_length=300)
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
```

`ForeignKey`を設定したモデルには新たに`親モデル名_id`というカラムが追加されます。
この場合は、コメントモデルに`blog_id`カラムが追加されます。

`on_delete=models.CASCADE`は、親が削除された際に子も削除するためのオプションです。
`on_delete`に設定できるオプションは他にも色々と用意されています。
他のオプションについては[この記事](https://djangobrothers.com/blogs/on_delete/)で非常にわかりやすくまとめられています。

`models.py`を編集した内容を反映させるためのマイグレーションファイルを作成する。

```
$ python manage.py makemigrations
```

マイグレーションファイルの内容をデータベースに反映する

```
$ python manage.py migrate
```

### フォームの追加

[blog_app/views.py]

```py
from .models import Comment

# 省略

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['content']
```

### テンプレートの編集

今回は、ブログの詳細画面にコメントを投稿できる機能を追加します。


[blog_app/templates/blog_app/detail.html]

```html
<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>Detail</h1>
        <table>
            <tr>
                <th>ID</th>
                <td>{{obj.id}}</td>
            </tr>
            <tr>
                <th>TITLE</th>
                <td>{{obj.title}}</td>
            </tr>
            <tr>
                <th>CONTENT</th>
                <td>{{obj.content}}</td>
            </tr>
        </table>
        <!-- 以下を追加 -->
        <form action="{% url 'detail' id %}" method="post">
            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit" value="click">
        </form>
        {% for comment in comments %}
        <p>{{comment.content}}</p>
        {% endfor %}
        <!-- ここまで -->
    </body>

</html>
```

### ビューの修正

[blog_app/views.py]

```py
# 省略
from .models import Comment
from .forms import CommentForm


def detail(request, num):
    blog = Blog.objects.get(id=num)
    comments = blog.comment_set.all()
    if (request.method == 'POST'):
        obj = Comment(blog_id=num)  # ポイント1
        comment = CommentForm(request.POST, instance=obj)
        comment.save()
        return redirect(to=f'/blog_app/detail/{num}')
    params = {
        'id': num,
        'obj': blog,
        'form': CommentForm(),
        'comments': comments,
    }
    return render(request, 'blog_app/detail.html', params)
```

ポイント1：コメントのインスタンス作成時に、生成するコメントの`blog_id`に`num`を入れておくことで、ブログとのアソシエーションしたコメントが生成される。

サーバを起動してコメントが投稿できるようになていればOK。

## 確認ダイアログの実装

削除ボタンを押した際に、削除用のページに遷移することに違和感があるので、確認ダイアログを表示させる仕様に変更します。

[blog_app/templates/blog_app/index.html]

```html
<!-- 省略 -->
<td><a href="{% url 'delete' item.id %}" onclick='return confirm("本当に削除しますか？");'>Delete</a></td>
<!-- 省略 -->
```

`delete`関数を以下のように編集。

[blog_app/views.py]

```py
def delete(request, num):
    blog = Blog.objects.get(id=num)
    blog.delete()
    return redirect(to='/blog_app')
```

あとは、不要になった`delete.html`を削除しましょう。