DjangoのModelとFormView、HTMLのform要素の関係性を理解する

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

最近、DjangoやHTML等のWebアプリケーション周りの勉強を強化しています。
この記事ではタイトルの通り、DjangoのModelとFormについて、HTMLのform要素について関係性をまとめ、理解していきたいと思います。

# DjangoにおけるFormとHTMLにおけるForm
結論から書いてしまうと、要素としては関連しているけれども概念としては若干異なるものみたいです。

HTMLのForm要素を使用すると以下のようなユーザに入力を求められるWebページを作成することが可能となります。別の表現をすると「ユーザからの入力データを受け付けるためのコンテナ」というみたいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/66f2653e-9b09-75d4-030d-4f2d549b07c3.png)
上記のHTML/CSSコードは以下を参照してください。

https://qiita.com/ohtsuka-shota/items/2d9f30ab32e2cb15abe2


一方でDjangoにおけるFormとはPythonのクラスで、ユーザからのデータの受け取りとバリデーション（≒データが基準や要件を満たしているかを確認するプロセスのことらしい。）、データの保存などを提供するものを示しているようです。またFormと言っていますが、FormViewと書いたほうが適切みたいです。

それぞれの関係性をざっくりと図示すると以下のようになると思われます。
エンドユーザはHTMLを閲覧し、form要素に必要事項を記入します。formのactionがキックされると、urls.pyを参照してviews.pyに具体的な処理を確認しに行く。今回はforms.pyにHTMLに記載されたデータを引き渡して妥当性が確認されると、models.pyで定義されたDBにデータを格納する。そんなイメージを今は持っています。
![django-ページ8.drawio (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/d4d4cee9-ade1-0355-022a-a0c56896d34b.png)


# github
以下に今回作成したものを掲載しております。
適宜ご覧ください。

https://github.com/ohtsuka-shota/publicDjangoStudy

# 環境構築
## 環境イメージ
今回は以下の環境で構築しております。formTestPJという名前のプロジェクトとMyFormという名前のアプリを作成しています。
![django-ページ9.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/f57e2653-413a-aa9d-f8ea-f5a1df00cc6d.png)

## 個人的に重要そうだと思う部分のメモ
### MyForm/urls.py
```MyForm/urls.py
from django.urls import path
from .views import MyFormView

app_name = "MyForm"

urlpatterns = [
    path('', MyFormView.as_view(), name="MyFormPage"),
]
```
- app_nameとpathのnameのところは使用するので、記載しないといけないものだと思います。

### MyForm/views.py
```MyForm/views.py
from django.urls import reverse_lazy
from django.views.generic import FormView
from .forms import MyForm

class MyFormView(FormView):
    template_name = "MyForm.html"
    form_class = MyForm
    success_url = reverse_lazy("MyForm:MyFormPage")

    def form_valid(self, form):
        form.save()  
        return super().form_valid(form)
```

- ユーザからの入力を受け付ける処理を実装する為にviews.pyでFormViewをimportして継承しています。
    - FormViewの他にCreateViewというものでも実現できる？らしい。この辺りは今後コーディングしてみたいと思います。
- form_class = MyFormではforms.pyで定義している処理の中でどの処理を使用するか、ということを明示しているイメージです。
- reverse_lazy("MyForm:MyFormPage")の""内はそれそれアプリ側のurls.pyに書かれている"app_name = "MyForm"の部分と"path('', MyFormView.as_view(), name="MyFormPage"),"から取ってきています。
-  def form_valid(self, form):
form.save()  
formに書かれていることが妥当であると判断された場合、そのフォームに記載されている内容を保存する。という意味らしいです。

```MyForm/forms.py
from django import forms
from .models import MyFormTable

class MyForm(forms.ModelForm):
    class Meta:
        model = MyFormTable
        fields = ['username', 'password', 'email', 'memo']
        widgets = {
            'password': forms.PasswordInput(),
        }
```
- modelはmodels.pyで定義しているMyFormTableを定義しています。こうすることでここで定義しているMyFormクラスは、データが妥当だと判断された場合ここで定義しているmodelにデータを保存するようになるようです。
- fieldはmodelに記載されているどの値にデータを格納するのか、ということを示しているようです。この値はもちろんmodels.pyで定義されているものを使用しなくてはいけません。
- "'password': forms.PasswordInput(),"の部分は、どうもブラウザ上でパスワードを隠すための設定らしいです。

```MyForm/models.py
from django.db import models

class MyFormTable(models.Model):
    username = models.CharField(max_length=150, unique=True)
    password = models.CharField(max_length=255)
    email = models.EmailField(unique=True)
    memo = models.CharField(max_length=255)
```
- 格納するデータについてそれぞれ定義をしている感じになりますね。
forms.pyのところでも記載をしておりますが、forms.pyのfieldと名前が一致している必要があります。
- memoの定義でCharfiledを指定していますが、Textfieldとしたほうが適切だと思います。スミマセン💦

```templates/MyForm.html
~~~中略~~~
<form method="post" action="{% url 'MyForm:MyFormPage' %}" class="sign-up-group">{% csrf_token %}

```
- "{% url 'MyForm:MyFormPage' %}"はviews.pyで記載しているのと同様にurls.pyで指定しているものになります。ここで指定をすることで、HTMLで定義したformが実行される時に何を呼び出すのか、ということを定義することが出来るようです。今回はMyFormアプリのMyFormPageとnamedされているものを呼び出します。

# 実行結果
入力フォームは以下となっております。
上からユーザ名、パスワード、メールアドレス、メモですね。
Try MyFormボタンを押下してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/17e70416-e1ee-8e26-08fc-c6ca9035b701.png)
views.pyのsuccess_urlでreverse_lazy("MyForm:MyFormPage")と定義していることで、元のMyFormのページにリダイレクトされました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/eb2ffcb5-08b3-4102-16be-faf68c379e12.png)
ユーザが登録されていることを確認してみます。
admin管理画面にログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/67da6efa-8eb2-106f-eb01-e3db7e1fc14c.png)
登録されていますね。ちゃんちゃん。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/ebfbfe1c-a50f-5c65-b945-e9b516925420.png)