概要(Django のフォーム=「入力フォームとバリデーションをまとめて面倒見てくれる仕組み」)
Django のフォームは、
「HTML フォームの入力項目」と
「その値のチェック(バリデーション)」と
「エラー表示」
を、ひとまとめに扱うための仕組みです。
生の HTML と手書きバリデーションでやろうとすると、
すぐにコードがぐちゃぐちゃになりますが、
Django のフォームを使うと、
どんな項目があるか
どんな型か
必須かどうか
どんなエラーを出すか
を Python のクラスとして定義できて、
テンプレート側では「フォームを描画する」ことに集中できます。
ここから、
フォームの基本構造
ビューとの連携
テンプレートでの表示
ModelForm との違い
設計のポイント
を順番にかみ砕いていきます。
なぜフォームクラスが必要なのか(生の HTML+手書きバリデーションのつらさ)
HTML だけでフォームを作ると何が起きるか
例えば、「名前とメールアドレスを入力してもらうフォーム」を考えます。
テンプレートにこう書くとします。
<form method="post">
{% csrf_token %}
<label>名前: <input type="text" name="name"></label>
<label>メール: <input type="email" name="email"></label>
<button type="submit">送信</button>
</form>
ビュー側では、POST された値をこう扱います。
from django.shortcuts import render
from django.http import HttpResponse
def contact(request):
if request.method == "POST":
name = request.POST.get("name")
email = request.POST.get("email")
# ここでバリデーションを自分で書く
errors = []
if not name:
errors.append("名前は必須です。")
if not email:
errors.append("メールは必須です。")
# メール形式チェックも自分で書く?
if errors:
return render(request, "contact.html", {"errors": errors})
return HttpResponse("OK")
return render(request, "contact.html")
Python最初はこれでも動きますが、
項目が増えたり、チェックが複雑になると、
必須チェック
型チェック(整数かどうか、メール形式かどうか)
エラーメッセージの管理
入力値の再表示
などを全部自分で書くことになり、
ビューが一気に肥大化します。
ここで「フォームクラス」にまとめると、
これらを Django に任せられるようになります。
Django フォームの基本構造(forms.Form)
フォームクラスを定義する
まずは、モデルと関係ない「純粋なフォーム」から始めます。
forms.py をアプリ内に作り、こう書きます。
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(label="名前", max_length=100)
email = forms.EmailField(label="メールアドレス")
message = forms.CharField(label="メッセージ", widget=forms.Textarea)
Pythonここでやっていることは、
name は文字列で、最大 100 文字
email はメール形式の文字列
message はテキストエリア
という「入力項目の定義」です。
重要なのは、
この時点で「バリデーションのルール」もほぼ決まっていることです。
CharField は「空はダメ(デフォルト必須)」
EmailField は「メール形式でなければエラー」
max_length を超えたらエラー
といったチェックを、Django が自動でやってくれます。
ビューでフォームを使う流れ
次に、このフォームをビューで使います。
from django.shortcuts import render
from django.http import HttpResponse
from .forms import ContactForm
def contact(request):
if request.method == "POST":
form = ContactForm(request.POST)
if form.is_valid():
name = form.cleaned_data["name"]
email = form.cleaned_data["email"]
message = form.cleaned_data["message"]
# ここでメール送信などの処理をする
return HttpResponse("送信しました")
else:
form = ContactForm()
return render(request, "contact.html", {"form": form})
Pythonここでの重要ポイントを整理します。
POST のときは ContactForm(request.POST) として「送信されたデータでフォームを作る」form.is_valid() を呼ぶと、全フィールドのバリデーションが実行される
バリデーションが通れば form.cleaned_data に「型が整えられた値」が入る
GET のときは ContactForm() で空のフォームを作る
ビュー側は、
「フォームを作る」
「バリデーションを走らせる」
「OK なら処理する、ダメなら同じテンプレートを再表示する」
という流れだけを意識すればよくなります。
テンプレートでフォームを表示する(form.as_p から始める)
最も簡単な描画方法:{{ form.as_p }}
contact.html をこう書いてみます。
<h1>お問い合わせ</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">送信</button>
</form>
{{ form.as_p }} は、
フォームの各フィールドを <p> タグで囲んだ HTML に変換してくれる簡易メソッドです。
これだけで、
ラベル
入力欄
エラーメッセージ
を含んだフォームが自動で描画されます。
最初はこれで十分です。
「フォームクラスを定義して、ビューで渡して、テンプレートで {{ form.as_p }}」
という流れを体で覚えましょう。
各フィールドを個別に描画する
少し慣れてきたら、
フィールドごとに細かく制御したくなります。
例えば、こう書けます。
<form method="post">
{% csrf_token %}
<div>
{{ form.name.label_tag }}
{{ form.name }}
{{ form.name.errors }}
</div>
<div>
{{ form.email.label_tag }}
{{ form.email }}
{{ form.email.errors }}
</div>
<div>
{{ form.message.label_tag }}
{{ form.message }}
{{ form.message.errors }}
</div>
<button type="submit">送信</button>
</form>
ここでのポイントは、
form.name は「name フィールドの input 要素」form.name.label_tag は「ラベル(<label>)」form.name.errors は「そのフィールドのエラーメッセージ」
という対応です。
この書き方をすると、
CSS クラスを付けたり、
レイアウトを細かく調整したりしやすくなります。
バリデーションの仕組みをもう少し深掘りする
is_valid() と cleaned_data の関係
form.is_valid() を呼ぶと、
Django は次のことをします。
各フィールドの型チェック(EmailField ならメール形式かどうかなど)
必須チェック(required=True のフィールドが空でないか)
max_length などの制約チェック
フォーム全体のカスタムバリデーション(後述)
すべてのチェックが通ると True を返し、
そのときだけ form.cleaned_data に「きれいな値」が入ります。
例えば、IntegerField なら文字列から整数に変換された値、EmailField なら前後の空白が削られたメールアドレス、
といった具合です。
逆に、エラーがある場合は False を返し、form.errors にエラーメッセージが入ります。
テンプレートで {{ form.errors }} や {{ form.field.errors }} を表示できます。
フィールドごとのカスタムバリデーション(clean_フィールド名)
例えば、「名前は ‘test’ を禁止したい」というような
独自ルールを追加したいとします。
フォームクラスにこう書けます。
class ContactForm(forms.Form):
name = forms.CharField(label="名前", max_length=100)
email = forms.EmailField(label="メールアドレス")
message = forms.CharField(label="メッセージ", widget=forms.Textarea)
def clean_name(self):
value = self.cleaned_data["name"]
if value.lower() == "test":
raise forms.ValidationError("この名前は使用できません。")
return value
Pythonclean_フィールド名 というメソッドを定義すると、
そのフィールドのバリデーションの最後に呼ばれます。
ここでエラーを投げると、そのフィールドにエラーメッセージが付きます。
重要なのは、
フォームクラスの中に「バリデーションロジック」を閉じ込められる
ビュー側は is_valid() を呼ぶだけでよい
という構造です。
ModelForm(モデルとフォームを一気に結びつける)
モデルとフォームが対応している場合は ModelForm が便利
例えば、Book モデルがあるとします。
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
price = models.IntegerField()
Pythonこの Book を作成・編集するフォームを作りたいとき、
わざわざ forms.Form で同じフィールドを二重に書くのは面倒です。
そこで使うのが forms.ModelForm です。
from django import forms
from .models import Book
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ["title", "price"]
Pythonこれだけで、
title は CharField
price は IntegerField
というフォームが自動で生成されます。
モデルのフィールド定義を元に、
フォームのフィールドとバリデーションルールが作られるイメージです。
ModelForm を使ったビューの流れ
Book を新規作成するビューはこう書けます。
from django.shortcuts import render, redirect
from .forms import BookForm
def create_book(request):
if request.method == "POST":
form = BookForm(request.POST)
if form.is_valid():
form.save() # Book インスタンスを DB に保存
return redirect("book_list")
else:
form = BookForm()
return render(request, "book_form.html", {"form": form})
Pythonここでのポイントは、
form.save() が「モデルインスタンスを作って保存する」までやってくれる
フォームのバリデーションとモデルの保存が一体化している
ということです。
ModelForm を使うと、
フォーム定義
バリデーション
モデル保存
をかなり少ないコードでまとめられます。
設計のポイント(フォームに何を任せて、ビューに何を残すか)
フォームは「入力項目とバリデーションの責任者」
フォームクラスに任せるべきことは、
どんな項目があるか
型や制約(必須、最大長、形式)
フィールドごとのバリデーション
フォーム全体の整合性チェック
です。
ビューは、
どのフォームを使うか決める
GET なら空フォーム、POST なら送信データ付きフォームを作るis_valid() を呼んで、OK ならビジネスロジックを実行する
テンプレートにフォームを渡して描画させる
という「流れの制御」に集中させると、
役割分担がきれいになります。
「フォームを通さない入力」は基本的に危険だと意識する
Django では、
ユーザーからの入力(POST データなど)は、
基本的に「フォームを通して検証する」
という前提で設計されています。
フォームを使わずに request.POST を直接いじりまくると、
必須チェック漏れ
型変換ミス
エラーメッセージの表示忘れ
などが起きやすくなります。
「ユーザー入力があるなら、まずフォームを考える」
という癖をつけておくと、安全で読みやすいコードになりやすいです。
まとめ(Django のフォームは「入力とバリデーションの全部を引き受ける窓口」)
Django のフォームを初心者向けに整理すると、こうなります。
フォームクラス(forms.Form / forms.ModelForm)は、「どんな入力項目があり、どんな型・制約・バリデーションが必要か」を Python のクラスとして表現する仕組み。
ビューでは、GET で空フォームを作り、POST で送信データ付きフォームを作り、is_valid() でバリデーションを走らせ、OK なら cleaned_data や form.save() を使って処理する、という流れになる。
テンプレートでは {{ form.as_p }} でざっくり描画するところから始め、慣れてきたら {{ form.field }} や {{ form.field.errors }} を使って細かくレイアウトを制御できる。
ModelForm を使うと、モデルのフィールド定義からフォームが自動生成され、バリデーションと保存処理をかなり少ないコードで書ける。
設計の肝は、「ユーザー入力はフォームに通す」「フォームは入力とバリデーションの責任者、ビューは流れの責任者」という役割分担を意識すること。
