【Python Django】初心者プログラマーのWebアプリ#4 【フォーム送信】

第四回目Python Djangoフレームワークを使ったフォーム機能を実装。
初心者プログラマー用のwebアプリ作成チュートリアル始めます。前回は画像, CSS, JavaScriptを使う方法でした。

https://qiita.com/Bashi50/items/3452bd1692fdd0ac6392

フォームを使えるようになると入力情報してもらうことで一般的なWEBアプリだけでなく、AI、画像認識機械学習、自然言語系の機械学習だろうが入力されたフォームの情報を使って役に立つWebアプリで使えるようになると思います。

Pythonを使っているならぜひ身につけておきたいところですね。
![pngwing.com (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/bca13665-497b-aef7-5c09-10ea6792f50d.png)

:::note info
内容が古くなったのでDjango 3で記事更新しました。 updated: 2021/09
:::

# :pushpin: Pythonで作るDjangoアプリ記事一覧

|  | 内容|
|:----:|:----:|
|**part1**|[簡単な文字表示アプリを作る](https://qiita.com/Bashi50/items/c686d30fa4fdba4582ad)|
|**part2**|[HTMLテンプレート表示](https://qiita.com/Bashi50/items/b9666030a058795c7dd8)|
|**part3**|["画像" "CSS" "Javascript"実装](https://qiita.com/Bashi50/items/3452bd1692fdd0ac6392)|
|**part4**|**<font color="red">フォーム送信 ← ココ</font>**|
|**part5**|[データベースの値取得・更新](https://qiita.com/Bashi50/items/280bc341eeec5d7f8797)|
|**番外編**|[Django AWS デプロイ](https://qiita.com/Bashi50/items/d5bc47eeb9668304aaa2)|
|**APIサーバー編**|[Django REST frameworkとReact #4](https://qiita.com/Bashi50/items/203505440049d759b694)|
|**単体テスト編**|[Django unittestで単体テスト](https://qiita.com/Bashi50/items/4205c398f475bc69f631)|

## ソースコード
この章のコードは以下です。確認やコピペでどうぞ

https://github.com/kohishibashi/easy-django/tree/part4

# Djangoのフォーム機能が便利
Djangoを使うなら便利なform機能を使って作りましょう。
入力エラーのチェックなど実装されていますし、コードも少なく作れます。

## Djangoのformって何？
HTMLのformといえば、MDNより文字の入力なら


https://developer.mozilla.org/ja/docs/Web/HTML/Element/input

選択フォームなら

https://developer.mozilla.org/ja/docs/Web/HTML/Element/select

こんな感じです。CSSで装飾されてないので見た目は違うかもしれないですけど、HTMLのformといえばこんなものですね。

DjangoのformはHTMLのformの要素なんかを作ることもできるんですが、Djangoのformで本当に大事なのは次に示すようなことです。

### Djangoのformの機能

- 入力（選択）されたデータにエラーがないかチェックできる
- 入力できるデータの型とか形式を定義できる
- エラーメッセージなどを活用できる
- HTMLのform要素から得られたデータだけだとPythonで使うには不便なことあるのでデータの加工するのも任せよう

なんてのが本質的な機能です。

:::note warn
ただ、今回は初歩的な内容を扱うのでフォームを作るくらいにとどめます。すみません。。。
:::

## forms.py作成
では、さっそく作っていきましょう！`forms.py`ファイルを作成します。
`C:\Users\○○○\django\testproject\webtestapp\forms.py`


```testproject/webtestapp/forms.py
from django import forms

class TestForm(forms.Form):
    text = forms.CharField(label='文字入力')
    num = forms.IntegerField(label='数量')
```

`forms.Form`クラスを継承して`TestForm`というのを作ってみました。

:::note info
とりあえず難しいことはいいので、forms.Formクラスというのにformのデータチェックとか、データの定義とかするための部品がたくさん入っていると思っておけばいいと思います。
:::

で、部品を使って自分の作りたいデータ入力できる機能を作る定義を追加していくことになります。

定義したのは、

- `forms.CharField`はテキストを入力するフォーム。
- `forms.IntegerField`は数値(整数)入力するフォーム。

labelは表示されたときにフォーム横に表示されるテキスト。HTMLにlabelタグありますね。あれです。

これで、HTMLテンプレートに埋め込んだときにいい感じで判断してくれて、

- `forms.CharField`なら文字入力フォーム
- `forms.IntegerField`なら数値を▲ ▼押したら増減できるフォーム

を作れます。楽です。
↓文字入力と数値入力フォーム
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/431ff413-e2f7-5b07-1b4a-95968fb76ece.png)

:::note warn
以下からは、前回までの続きなので今回のフォーム送信には関係無いものもありますので適時読み飛ばしや追加などしてください。
:::

## views.pyに記述
では、最初にformがどんな感じか見るために画面に表示させてみましょう。

```testproject/webtestapp/views.py
from django.shortcuts import render
from .forms import TestForm  # 追加


def index(request):
    my_dict = {
        'insert_something':"views.pyのinsert_something部分です。",
        'name':'Bashi',
        'test_titles': ['title 1', 'title 2', 'title 3'],
        'form': TestForm(),  # 追加
    }
    return render(request, 'webtestapp/index.html', my_dict)
```

追加箇所は、

1. さっき作成した`forms.py`を使いたいのでインポート
2. テンプレートに渡して画面に入力フォームを表示したいので辞書の中に入れる。

```testproject/webtestapp/templates/webtestapp/index.html
.
.
.
    <h1>index.html表示してます</h1>
    <img src="{% static "images/test_img.png" %}" alt="表示できません">
    <p>{{ insert_something }}</p>
    <p>作成者は<b id='test'>{{ name }}</b>です。</p>

    <form action="{% url 'webtestapp:index' %}" method="post">
        {% csrf_token %}
        {{ form }}
        <input type="submit" value="送信">
    </form>
    
    <p>作成者は<b id="test">{{ name }}</b>です。</p>
.
.
.

```

↓みたいな表示が作れると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/d666dfd9-52fd-6d0c-2e74-ae9b958a5ae1.png)

### 追加箇所の解説

#### 1. formの送り先
`<form action="{% url 'webtestapp:index' %}" method="post">`
HTMLのformで、送信先だけDjangoのテンプレート機能にて埋め込んでいる感じです。

ただ、このままだと、見た目ができただけなので送信ボタンを押しても何も起こりません。

#### 2. フォーム使った攻撃対策
次に、`{% csrf_token %}`という、最初見たら意味不明なのが書かれています。
簡単に触れますが、フォーム使った攻撃対策に必要な一文になります。これを自分で対策しようとすると結構面倒なのですが、よく使うのでDjangoが用意してくれています。
とりあえずいまは理解はいいので使っておきましょう。無いとエラーですし。

#### 3. form 要素
`{{ form }}`は`views.py`のテンプレートに埋め込むための変数の`my_dict`の中に入れたデータです。
importしてdictにいれただけなので、`forms.py`で作れたフォームを埋め込んだということですね。
あと、送信ボタンはinput タグで追加してます。

### views.pyに入力されたデータの処理記述
いまの状態だと送信ボタンを押しても何も起こらないので、送信ボタンを押してデータ送信したときDjangoが処理するデータ処理を書いていきましょう。

```testproject/webtestapp/views.py
from django.shortcuts import render
from .forms import TestForm

def index(request):
    my_dict = {
    'insert_something': "views.pyのinsert_something部分です。",
    'name': 'Bashi',
    'form': TestForm(),
    'insert_forms': '初期値',  # 追加
    }
    # ---- 追加 ----
    if (request.method == 'POST'):
        my_dict['insert_forms'] = '文字列:' + request.POST['text'] + '\n整数型:' + request.POST['num']
        my_dict['form'] = TestForm(request.POST)
    # ---- 追加 ----

    return render(request, 'webtestapp/index.html', my_dict)
```

`{{　insert_forms|linebreaksbr }}`は`insert_forms`表示用に追加してます。

```testproject/webtestapp/templates/webtestapp/index.html
    <p>{{　insert_forms|linebreaksbr }}</p>
    <form action="{% url 'webtestapp:index' %}" method="post">
      {% csrf_token %}
      {{ form }}
      <input type="submit" value="送信">
    </form>
```

1. `insert_forms`というのを追加しました。データを送信する前には「初期値」というのが表示
1. 送信したあとはPOST送信されるのでif文の`my_dict['insert_forms']`の内容が入ります。データを使って、`insert_forms`にデータを表示するための文字列を入れています。
1. `my_dict['form'] = TestForm(request.POST)`で値を上書き。これを書くとフォームに入力した情報が残っているので送信ボタン押した後にも値を保持できて便利。
1. `{{ insert_forms|linebreaksbr }}`の`linebreaksbr`というのはテンプレートで使えるタグです。`\n`が改行を表す記号なのですが、HTMLでは機能しません。なので`linebreaksbr`を使うことでHTMLの`<br>`タグに変えています。

:::note info
{{ insert_forms|safe }} は個人のローカルでテスト開発するなら別に必要ないです。不特定多数の人が触れる環境にデプロイするなどするならこのような箇所は気を付けましょう。
:::

![キャプチャ.JPG](https://qiita-image-store.s3.amazonaws.com/0/260345/37cf60b6-ff12-27e2-6124-658c14a22e9e.jpeg)

これでとりあえずできました。「初期値」の部分が2つのフォームに入力したものに変わります。
![キャプチャ.JPG](https://qiita-image-store.s3.amazonaws.com/0/260345/66a98fe0-e5a5-58af-4d0c-32ac8707b087.jpeg)

## 少しキレイに整える

今回はフォームが2つなのでそうでもないですが、
フォームが増えると1行にフォームが並び、汚くなります。

`.as_table`とすると`<tr><td>`タグでくくってくれます。
ついでに`.as_ul`だと`<li>`でくくります。


```testproject/webtestapp/templates/webtestapp/index.html
    <p>{{　insert_forms|linebreaksbr }}</p>
    <table>
      <form action="{% url 'webtestapp:index' %}" method="post">
          {% csrf_token %}
          <tr>
            <td></td>
            <td>入力フォーム</td>
          </tr>
          {{ form.as_table }}
          <tr>
            <td></td>
            <td><input type="submit" value="送信"></td>
          </tr>
      </form>
    </table>
```
![キャプチャ.JPG](https://qiita-image-store.s3.amazonaws.com/0/260345/566394a1-bb73-23b0-b9e7-610c324af1fa.jpeg)

完成。ブラウザにて数値や文字列を渡せるのはアプリケーションを作る上でとても重要ですのでぜひ使っていただきたいです！


# 補足事項
## forms.pyについて
今回forms.pyではエラーチェックとか制限などもうけなかったのですが、本来のforms.pyで行うのはそのようなバリデーション機能やデータの加工機能です。今後、追加していこうと思います。

## formの送り先でapp_name指定した理由
`<form action="{% url 'webtestapp:index' %}" method="post">`
`{% url '名前' %}`という書き方でURLを書く。

```python:urls.py
app_name='webtestapp'
urlpatterns = [
    path('', views.index, name='index'),
]
```

`app_name`が`webtestapp`で`name='index'`なのでこのような書き方。

`app_name='webtestapp'`
を指定しないと`{% url 'index' %}`となる。

:::note info
今回は問題ないですが、アプリが増えた場合この書き方だと「index」というのが複数になったときに、<font color="red">どのindexを参照するの？？</font>とならないようにこのように書いています。
:::


# :pushpin: 一連の記事

|  | 内容|
|:----:|:----:|
|**part1**|[簡単な文字表示アプリを作る](https://qiita.com/Bashi50/items/c686d30fa4fdba4582ad)|
|**part2**|[HTMLテンプレート表示](https://qiita.com/Bashi50/items/b9666030a058795c7dd8)|
|**part3**|["画像" "CSS" "Javascript"実装](https://qiita.com/Bashi50/items/3452bd1692fdd0ac6392)|
|**part4**|**<font color="red">フォーム送信 ← ココ</font>**|
|**part5**|[データベースの値取得・更新](https://qiita.com/Bashi50/items/280bc341eeec5d7f8797)|
|**番外編**|[Django AWS デプロイ](https://qiita.com/Bashi50/items/d5bc47eeb9668304aaa2)|
|**APIサーバー編**|[Django REST frameworkとReact #4](https://qiita.com/Bashi50/items/203505440049d759b694)|

# 参考
この章のコードは以下です。確認やコピペでどうぞ

https://github.com/kohishibashi/easy-django/tree/part4