CreateViewが機能しなくて困った話


前回、[todolistをdjangoで作成する](https://qiita.com/maooz4426/items/c03dbcedcf0c4276f80c)記事を挙げたのですが、記事を上げた時点で新しいタスクが投稿できないバグに直面しました。エラー文を見てもよくわからず、とても困りましたがformの書き方に問題がありました。


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


#カテゴリモデル
class Category(models.Model):

    #カテゴリ名
    name = models.CharField(max_length=255)
    #カテゴリを設定した人
    author = models.ForeignKey(
        'auth.User',on_delete=models.CASCADE,
    )
    #作成日
    created_at = models.DateTimeField(auto_now_add=True)
    #更新日
    updated_at = models.DateTimeField(auto_now=True)

    #これ入れないと管理画面でcategoryobjectって名前になる
    def __str__(self):
        return self.name

class Task(models.Model):

    #タスク名
    title = models.CharField(max_length=255)
    # タスクを設定した人
    author = models.ForeignKey(
        'auth.User',
        on_delete=models.CASCADE,
    )
    #カテゴリ（外部キー参照)
    category = models.ForeignKey(
        Category, on_delete=models.PROTECT
    )
    #完了確認
    completed = models.BooleanField(default=False)
    #締切日
    deadline = models.DateTimeField()
    #タスク作成日
    created_at = models.DateTimeField(auto_now_add=True)
    #タスク完了日
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('todo:detail', kwargs={'pk': self.pk})

```
モデルはこのようになっていて、タスクの設定者を`auth.user`を外部キーとして、選択できるようになっていました。

しかし、`forms.py`は

```forms.py
from django import forms

from django.contrib.admin.widgets import AdminDateWidget

from .models import Task

class TaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = ['title', 'author', 'category', 'deadline', ]

        #ウィジェットはフォームとかの部品の種類を指定する要素
        #djangoのフォームウィジェットにHTML属性を付与する(attrs)
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'author': forms.TextInput(attrs={'class': 'form-control'}),
            'category': forms.Select(attrs={'class': 'form-control'}),  # カテゴリが選択肢の場合
            'deadline': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),

        }

```

`'author': forms.TextInput(attrs={'class': 'form-control'}),` となっており、コレが原因で投稿できなくなっていました。

これを以下のように`forms.Select`使うことで改善しました。

```forms.py
from django import forms

from django.contrib.admin.widgets import AdminDateWidget

from .models import Task

class TaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = ['title', 'author', 'category', 'deadline', ]

        #ウィジェットはフォームとかの部品の種類を指定する要素
        #djangoのフォームウィジェットにHTML属性を付与する(attrs)
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            
            'author': forms.Select(attrs={'class': 'form-control'}),#修正
            'category': forms.Select(attrs={'class': 'form-control'}),  # カテゴリが選択肢の場合
            'deadline': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),

        }
```
:::note
`forms.Select`で選択肢が準備されるのは、Metaクラスとして定義した際にデータベースから情報を参照して、以下のようなコードを内部的に実行しているかららしいです。
```
author = forms.ModelChoiceField(queryset=User.objects.all())
```
:::

コレに気づいたのは管理者画面で`task`を設定しようとしたら、セレクターになっていたのでそれで気づきました。
もし困った方がいたら、トラブルシューティンングで活用してください。