【Django】pollsアプリに新規質問作成機能を追加

#Djangoの公式ドキュメントで投票アプリを作成した後に機能を追加したい
[Djangoの公式ドキュメント](https://docs.djangoproject.com/ja/3.0/contents/)に沿って一通りフレームワークの仕組みを学んだ後、機能を追加してみたいと思い、実装してみることにした。


##はじめに
・[Djangoの公式ドキュメント](https://docs.djangoproject.com/ja/3.0/contents/)では、簡単な投票アプリを１から作成しながらDjangoに触れ、学ぶことができる。

・Djangoの公式ドキュメントで初めてDjangoに触れてみたが、初学者にとっては解説が丁寧ではないと感じ、アプリ作成の途中で本チュートリアルで学ぶことを停止し、[UdemyのDjango 教材](https://www.udemy.com/course/django-3app/)（Djangoで検索して一番売れていたもの）で基礎の基礎を学ぶことにした。

・Udemyの教材でDjangoの基礎の基礎を学んだ後、公式ドキュメントに立ち返り投票アプリを作成完了させた。


##環境
・Python 3.8.2
・django 3.0.3
・OSはWindows10とmacOSの両方

##追加機能作成（質問＆選択肢の新規作成）

公式ドキュメントで作成した投票アプリは、質問と選択肢を追加するために管理ユーザー画面を使わなければならない。
一般ユーザー画面でも新規作成画面を追加しようと思う。

##実装方法
・CreateViewを用いた実装を行う
・質問一覧ページに質問作成ページへのリンクを作成する
・最初にQuestionテーブルに新規質問を作成する
・その後に、質問に紐づいた新規選択肢を作成する

##完成イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605244/dec6393a-be86-b7b5-1e3c-09fadc810ea3.png)






##コード

###[views.py]

```python:polls/views.py
#importは省略

#省略

class QuestionCreate(CreateView):
    #template_name = 'polls/createquestion.html'
    model = Question
    fields = ('question_text',)
    success_url = reverse_lazy('polls:index')


class ChoiceCreate(CreateView):
    model = Choice
    fields = ('choice_text',)
    success_url = reverse_lazy('polls:index')

    def dispatch(self, request, *args, **kwargs):
        self.question = get_object_or_404(Question, pk=kwargs['question_id'])
        return super().dispatch(request, *args, **kwargs)

    def form_valid(self, form):
        form.instance.question = self.question
        return super().form_valid(form)

```
①テンプレートの名前を「 template_name = (任意の画面名).html 」　と記述しなかった（コメントアウトしている）のは、
画面名を「 モデル名\_form.html 」とすればCreateViewと画面が自動的に紐づくから。
※画面名を任意の名前にしたい場合は、template\_nameを指定する必要がある。


②fieldsの中に指定するカラムが１つの場合、カラム名の後にカンマ（ , ）が必要

③一番厄介なのは`ChoiceCreate`
`dispatch`は何をしているのかというと、、
既存のViewクラスのメソッドである`dispatch`をオーバーライドしている。

choiceはquestionと紐づけなければならないため、`dispatch`でquestion_idを指定している。

基本的に汎用ビュー（CreateViewなど）の中で`def XXXX():`といった形はオーバーライドしているケースが多いと考えられる。（異論は認めます。。。）

気になる方は、汎用ビューの定義を参照してみるといい。
※VScodeの場合は気になるメソッドやクラスを右クリックで「定義へ移動」をクリックすると上位クラスの中身が見られる。

###[urls.py]

```python:polls/urls.py
#importは省略

app_name = 'polls'
urlpatterns = [

    #公式ドキュメントと同様のため省略

    path('question_create/', views.QuestionCreate.as_view(),
        name='questioncreate'),

    path('<int:question_id>/choice_create/', views.ChoiceCreate.as_view(),
        name='choicecreate'),
]
```

###[models.py]

```python:polls/models.py

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published', auto_now_add=True)

    def __str__(self):
        return self.question_text
```
pub_dateの第二引数に`auto_now_add=True`を追加する。

###[templates]
```django:polls/templates/polls/index.html
{% if latest_question_list %}
<ul>
    {% for question in latest_question_list %}
    <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a>
    {% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

<a href="{% url 'polls:questioncreate' %}">New Question</a>
```

最後の行に、新規質問作成画面へのリンクを追加する。

```django:polls/templates/polls/question_form.html

<form method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Save">
</form>
```
```django:polls/templates/polls/choice_form.html

<form method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Save">
</form>
```


##最後に

最後までご覧いただきありがとうございました！
本記事は私のQiita初投稿のため、つたない部分も大いにあったと思いますが、同じような機能追加に困っている方の一助になればと思います。
私はこの実装に結構時間がかかったので。。。

基本的に汎用ビューは１つのモデルに対応しています。
今回のアプリは２つのテーブルに新規の内容を追加しなければならないため、質問作成と選択肢作成を別々で実装しました。


本当は１つの画面で質問とそれに紐づく選択肢を作成したかったのですが、私のいまの技術では実装することができませんでした。
QuestionとChoiceのテーブルを結合してしまえばできたのかもしれませんが、、、