[Django][Python]FormViewの使い方

# FormViewとは
具体的には以下のような画面
https://sample-apps.box16.site/morpholy/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023362/673f0d13-d4af-ba6b-76a1-0c31b62bd473.png)

(上記のWebアプリは自分が作ったもので、入力した文字列を形態素解析するという簡単なもの
Github : https://github.com/box16/morpholy)

こういったForm(何かしらの情報を入力できるHTML要素)をまとめて1画面にできるDjangoの汎用ViewがFormView
詳細は以下から確認できます
https://docs.djangoproject.com/en/3.1/ref/class-based-views/generic-editing/#django.views.generic.edit.FormView

# FormViewの使い方

### 1. forms.pyを作成する
まず、どんなFormを用意するのかを記述するために`forms.py`を作成します
配置するのはアプリディレクトリ配下

```
.
├── __init__.py
├── admin.py
├── apps.py
├── forms.py ← ここ！！
├── migrations
│── models.py
├── templates
│── tests.py
├── urls.py
└── views.py
```

```forms.py
from django import forms


class MorpholyForm(forms.Form):
    text = forms.CharField(widget=forms.Textarea,
                           label="解析対象")

    select_part = forms.MultipleChoiceField(
        widget=forms.CheckboxSelectMultiple,
        choices=[("名詞", "名詞"), 
                 ("動詞", "動詞"), 
                 ("形容詞", "形容詞"), ],
        label="出力項目",
        error_messages={'required': '出力項目を選んでください'})

```

`forms.Form`を継承したクラスを作成します(今回の例では`MorpholyForm`)
クラス内に、formを定義していきます

今回の例では、文字列を入力させるための`forms.CharField`,形態素解析対象を選択する`forms.MultipleChoiceField`
を用意してます

### 2. views.pyでFormクラスを使う

まず、以下の画面をindexとして作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023362/ed27da91-6e28-9d01-f6c3-ebf05080a395.png)  

これの結果出力をresultとして作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023362/56f4884c-7366-2b0f-2c35-ba858b0a5839.png)



```views.py
from django.shortcuts import render
from django.http import HttpResponse
from django.views.generic.edit import FormView
from .forms import MorpholyForm
from .extensions import NLP

nlp = NLP()

# postされた情報を一時的に格納するためのディクショナリ
posted_data = {"text": "",
               "select_part": []}


class IndexView(FormView):
    template_name = 'morpholy/index.html'
    form_class = MorpholyForm # forms.pyで作ったFormクラスをここで使う
    success_url = 'result' # formに入力された値が正しければこのURLに飛ぶ

    def form_valid(self, form):
        posted_data["text"] = form.data.get("text") # Formに入力された値を取得(getメソッドはリストの場合最後の値を取得)
        posted_data["select_part"] = form.data.getlist("select_part")# getlistメソッドはリストすべてを取得
        return super().form_valid(form)


def result_view(request):
    result = nlp.extract_parts(
        text=posted_data["text"],
        select_part=posted_data["select_part"])
    return render(request, 'morpholy/result.html', {"part_result": result})
```

### 3. viewと紐づけるTemplateの作成

index.htmlはIndexViewと紐づく
forms.pyで作成したFormクラスを以下のように埋め込める

```index.html
<form method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Send message">
</form>
```

result.htmlはresult_viewに紐づく

```result.html
{% if part_result %}
    {% for part,result_list in part_result.items %}
        <h2>{{part}}</h2>
            <p>
                {% for result in result_list %}
                    {{result}} , 
                {% endfor %}
            </p>
    {% endfor %}
{% else %}
    <p>解析に失敗しました</p>
{% endif %}

<p>
    <a href="{% url 'morpholy:index' %}">入力画面に戻る</a>
</p>
```

### 4. urls.pyでルーティング

URLとViewの紐づけをurls.pyで行う

```urls.py
  
from django.urls import path

from . import views

app_name = "morpholy"
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('result', views.result_view, name='result'),
]
```

# まとめ
* FormViewはこんな感じのViewが作れる汎用View  
https://sample-apps.box16.site/morpholy/

* Formクラスを継承したサブクラスを用意する  
* formに入力された値は、get,getlistで取得する
* FormViewのテンプレートはめっちゃ簡単にかける