【Django】登録・編集フォーム（Form）でユーザーに紐づく登録データを表示する

## やりたいこと
Modelに外部キー（FOREIGN KEY）制約のある2つのテーブルを用意したとき、Formではユーザー（ログインユーザー）に紐づいた、登録データを表示したいです。
クラスベースの汎用ビューを使ったときの、やり方でつまずいたので、記録しておきます。

例えば、以下のようなTodoリストを作成します。
このとき、1つのTodo Itemには選択できるCategoryが複数あります。
![todo_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605279/c698230d-519b-a0b6-ea8b-f8ddd8af878a.png)

以下のように、ログインユーザーで登録しているCategoryが2つあるとします。
![todo_3-crop.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605279/46d54a1f-810c-9518-5846-777b0d22799e.png)


このとき（当然のことですが）、登録や編集のフォームで選択できるCategoryは、ログインユーザーが登録したCategoryのみとしたいです。

![todo_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605279/5c62a282-ce36-fbb3-77cc-b352f9c9cbc4.png)

## 環境
Python 3.8.0
Django 3.0.4
Bootstrap 4.1.3
プラグイン：[django-bootstrap-modal-forms](https://github.com/trco/django-bootstrap-modal-forms)
※一般的なCreateView、UpdateView等を使う場合も同様のやり方です

## やり方
[このページ](https://stackoverflow.com/questions/24041649/filtering-a-model-in-a-createview-with-get-queryset)を参考にしました。
### 使用するModel

```python

# mypeoject/myapp/models.py
import datetime
from django.db import models


class Category(models.Model):
    category_name = models.CharField(max_length=20, unique=True)
    user = models.ForeignKey(
        'auth.User',
        on_delete=models.CASCADE
    )

    def __str__(self):
        return self.category_name


class TodoItem(models.Model):
    item = models.CharField(max_length=50)
    item_date = models.DateField(default=datetime.date.today)
    categories = models.ForeignKey(Category, null=True, on_delete=models.SET_NULL)
    user = models.ForeignKey(
        'auth.User',
        on_delete=models.CASCADE
    )

    def __str__(self):
        return self.item

```
上記の例の場合、使用するModelには、TodoItemテーブルと、外部キー（FOREIGN KEY）制約のあるCategoryテーブルの2つを用意しています。

### forms.pyの編集

```python

# mypeoject/myapp/forms.py
from bootstrap_modal_forms.forms import BSModalForm
from django import forms

from queryset_filtering_app.models import TodoItem, Category


class CreateUpdateTodoItemForm(BSModalForm):
    class Meta:
        model = TodoItem
        fields = ['item', 'item_date', 'categories']

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user')
        super(CreateUpdateTodoItemForm, self).__init__(*args, **kwargs)
        self.fields['categories'].queryset = Category.objects.filter(user=user)


class CreateCategoryForm(forms.ModelForm):
    class Meta:
        model = Category
        fields = ["category_name"]

```
forms.pyは上記のように、def \_\_init\_\_（）の記述を追加します。

### views.pyの編集

```python

# mypeoject/myapp/views.py
class CreateTodoItemFormView(BSModalCreateView):
    template_name = 'template_name.html'
    form_class = CreateUpdateTodoItemForm
    success_message = 'Success: Item was created.'
    success_url = reverse_lazy('redirect_name')

    def get_form_kwargs(self):
        kwargs = super(CreateTodoItemFormView, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

    def form_valid(self, form):
        form.instance.user_id = self.request.user.id
        return super(CreateTodoItemFormView, self).form_valid(form)


class UpdateTodoItemFormView(BSModalUpdateView):
    model = TodoItem
    template_name = 'template_name.html'
    form_class = CreateUpdateTodoItemForm
    success_message = 'Success: Item was updated.'
    success_url = reverse_lazy('redirect_name')

    def get_form_kwargs(self):
        kwargs = super(UpdateTodoItemFormView, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs
```
views.pyでは上記のように、def get\_form\_kwargs（）の記述を追加します。
CreateViewのdef form\_valid（）の箇所は、登録時にログインユーザーをuserに登録するためのコードです。


## おわりに
登録・編集フォームでユーザーに紐づく登録データを表示する方法を記載しました。
基本的なところだと思いますが、私のような初心者だと、適切な記事を見つけられず、すぐには解決できないこともあるかと思うので、参考にしていただければと思います。