Djangoのクラスベースビューでテーブルの親や子を取得するメモ

# はじめに
productテーブルは `category` と `user` が ForeignKey という構造です。`category_id` と `user_id` の日本語を取得したいわけだ。意外に調べるのにてこずってしまった

# ER
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94562/87500cd5-bd94-fec3-e707-2d8ac261162a.png)

# model Productについて
```python:shop/models.py
class Product(models.Model):
    """商品"""
    name = models.CharField(verbose_name='名前', max_length=200, blank=False, null=False)
    category = models.ForeignKey(Category, on_delete=models.PROTECT)
    price = models.IntegerField(verbose_name='単価',)
    description = models.CharField(verbose_name='説明', max_length=100, blank=False, null=False)
    inactive = models.BooleanField(default=False)
    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    created_at = models.DateTimeField('作成日時', auto_now_add=True)
```

productテーブルのイメージは以下の通り

| # id |  name              |  price |  description                                                                                                                                                                                 |  inactive |  created_at                 |  category_id |  user_id |
|------|--------------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|-----------------------------|--------------|----------|
| 1    |  スズキ スペーシア | 10000  |  スペーシアスペーシアスペーシアスペーシアスペーシアスペーシアスペーシアスペーシアスペーシアスペーシアスペーシア                                                                              | 0         |  2020-10-25 05:08:28.207335 | 1            | 2        |
| 2    |  ホンダ N-BOX      | 10001  |  ホンダ N-BOXホンダ N-BOXホンダ N-BOXホンダ   N-BOXホンダ N-BOXホンダ N-BOXホンダ N-BOXホンダ N-BOXホンダ N-BOXホンダ N-BOXホンダ N-BOX                                                      | 0         |  2020-10-25 05:08:38.896394 | 1            | 2        |
| 3    |  ムーヴキャンバス  | 10002  |  ダイハツ   ムーヴキャンバスムーヴキャンバスムーヴキャンバスムーヴキャンバスムーヴキャンバスムーヴキャンバスムーヴキャンバスムーヴキャンバスムーヴキャンバスムーヴキャンバスムーヴキャンバス | 0         |  2020-10-25 05:08:51.028046 | 1            | 2        |
| 4    |  10式戦車          | 10003  |  10式戦車10式戦車10式戦車10式戦車10式戦車10式戦車10式戦車10式戦車10式戦車10式戦車                                                                                                            | 0         |  2020-10-28 14:38:56.227012 | 2            | 2        |
| 5    |  11式戦車          | 10004  |  11式戦車11式戦車11式戦車11式戦車11式戦車11式戦車11式戦車11式戦車11式戦車11式戦車                                                                                                            | 0         |  2020-10-28 14:52:18.824511 | 2            | 2        |

# model Discussionについて
```python:shop/models.py
class Discussion(models.Model):
    """会話"""
    chat = models.TextField(verbose_name='会話')
    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    created_at = models.DateTimeField('作成日時', auto_now_add=True)
```

discussionテーブルのイメージは以下の通り。product_id `5` つまり `11式戦車` について2名のユーザーが、それぞれ1件、合計2件のコメントがついている

| # id |  chat                                                 |  created_at                 |  product_id |  user_id |
|------|-------------------------------------------------------|-----------------------------|-------------|----------|
| 1    |  こんにちは。11戦車について詳しく内容を知りたいです。 |  2020-11-03 12:53:01.617835 | 5           | 3        |
| 2    |  11式なんてあったんですね！？                         |  2020-11-03 12:53:40.885594 | 5           | 1        |

# views.py
views.py は極めてシンプル

```python:views.py
class ProductDetailView(generic.DetailView):
    """/detail/post_pk でアクセス。product 詳細."""
    model = Product
```

# テンプレートにおける操作
商品名 `{{ product.name }}` は普通にわかるね。
`1:1` カテゴリー名は {{ product.<font color="red">category</font>.name }}[^注釈1]
`1:n` 商品に対する会話（＝子）は {% for discussion in product.<font color="red">discussion_set.all</font> %}[^注釈2]
`1:1` ユーザー名は {{ discussion.<font color="red">user</font>.email }}[^注釈3]
[参考：公式ドキュメント](https://docs.djangoproject.com/ja/3.1/ref/models/querysets/#prefetch-objects)

```html:product_list.html
{% extends "shop/base.html" %}
{% block content %}

    <h1>商品詳細</h1>
    <h2>{{ product.category.name }}</h2>
    <h3>{{ product.name }}</h3>
    <hr>
    {% for discussion in product.discussion_set.all %}
        <p>{{ discussion.chat }} {{ discussion.user.email }}({{ discussion.created_at|date:"Y/m/d H:i" }})</p>
        <hr>
    {% endfor %}
    <a href="{% url 'shop:discussion_create' product.pk user.id %}">投稿</a>

{% endblock %}
```

SQLAlchemyで生SQL使わなくてもこれでかなり自由自在なはず。

[^注釈1]: 兄弟
[^注釈2]: .all()の他に.filter()や.count()などを使って絞り込みやオブジェクト数をカウントしたりもできる。
[^注釈3]: 子の兄弟