フォームに必要な各部品やバリデーションの定義をします。
特にDBとの関連付けなしに使用できます。
基本的に アプリ/forms.py に記述します。
● 基本書式
Django の部品生成の特徴として[フィールド]属性と[ウィジェット]属性があります。
例で使われているテキストフィールドは forms.CharField のフィールド属性と forms.TextInput のウィジェット属性で、文字列型のテキストボックスとして作成されます。
例)
from django import
forms
#Django の Form クラスを継承する
class HogeForm(
forms.Form):
部品名 =
forms.
CharField(
label = '名前',
max_length = 20,
#自動で Trim するのをやめる
strip = False,
error_messages = {
#引数で定義しているバリデーション名をキーとしたメッセージを設定できる
'max_value':'でかすぎ~',
'min_value':'小さすぎ~',
'unique':'同じ値はダメ~',
#形式等が違うエラーはこのキーで大体いける。
'invalid':'形式が違う',
},
#初期値
initial = '書いてね',
#HTML5 の必須属性を設定する
required = False,
widget =
forms.
TextInput(
#生成されたタグの中に任意に属性を追加できる
attrs={'size':'40','style':'color:red;'}
)
)
#[clean_] + [部品名]のメソッドを記述すると独自のバリデーション定義ができます。
def
clean_部品名(self):
#対象の部品に入力等された値を取得できる
s = self.
cleaned_data['
部品名']
if s.strip() == '':
#エラーにしたい場合は raise でスローすれば良い(引数はメッセージとなる)
raise
forms.ValidationError('ダメよ~')
#エラーがない場合は正常な値を返却する
return s
● 部品定義色々
非表示 = forms.CharField(
initial = '書いてね',
required = False,
widget = forms.HiddenInput()
)
#self.cleaned_data['部品名']で数値が取得できる
数値 = forms.IntegerField(
label = '年齢',
min_value = 5,
max_value= 120,
required = False,
widget = forms.TextInput(
#IntegerField には max_length パラメータがないので、attrs に定義する
attrs = {'size':'10','maxlength':'3'}
),
error_messages = {
'max_value':'でかすぎ~',
'min_value':'小さすぎ~',
'invalid':'形式が違う',
}
)
日付 = forms.DateField(
label = '生年月日',
required = False,
input_formats = ['%Y/%m/%d',],
initial = '2000/01/01',
error_messages = {
'invalid':'形式が違う',
}
)
ラジオ = forms.ChoiceField(
label = '性別',
widget = forms.RadioSelect(),
choices=(('1','男'),('2','女'),('3','ほげ')),
initial = '2',
required = False,
)
セレクト = forms.ChoiceField(
label = '色',
widget = forms.Select(),
choices=(('0','-' * 3),('1','赤'),('2','青')),
initial = '2',
required = False,
)
#1つしかないチェックボックス
フラグ = forms.BooleanField(
label = '確認',
initial = True,
required = False,
)
チェックボックス = forms.CheckboxSelectMultiple(
label = '食べ物',
widget = forms.CheckboxSelectMultiple(),
choices = (('1','リンゴ'),('2','バナナ'),('3','いちご')),
initial = ['2','3'],
required = False,
)
エリア = forms.CharField(
label = '備考',
widget = forms.Textarea(attrs={'cols': 80, 'rows': 4}),
required = False,
)
ファイル = forms.FileField(
label = 'ファイル選択',
required = False,
)
● 汎用的なメソッド
これ以降のメソッドは全て、各カラム名専用のメソッドだが、clean メソッドは汎用的に利用する事ができる。
def
clean(self):
#全カラムの値を取得できる
a = self.cleaned_data
#全カラムの値を取得できる(上と同じ)
b = super().clean()
#個々のカラムの値も取得できる
c = self.cleaned_data['fname_k']
#第1引数:カラム名、第2引数:エラー文言で複数のバリデーションエラーを定義できる
self.
add_error('
hoge', 'ほげ')
self.
add_error('
foo', 'ふ~')
#エラーがある場合は、文言の HTML が取得できる ※type は辞書型
print(self.errors)
#辞書っぽくとれる。
print(self.errors['hoge'])
#self.cleaned_data を返却すれば良いかな。
return self.cleaned_data
● CharField
#正規表現を利用する場合はインポートする
import re
def clean_myname(self):
s = self.cleaned_data['myname']
#トリムする
s = s.strip()
if s == '':
raise forms.ValidationError('何か入力して~')
# 間に空白をいれたい場合は[ぁ-ん ]でよい
m = re.search(r'^[ぁ-ん]+$', s)
if m == None:
raise forms.ValidationError('ひらがなだけです!')
m = re.search(r'^[ァ-ヶー]+$', s)
if m == None:
raise forms.ValidationError('カタカナだけです!')
m = re.search(r'^\d{3}\-\d{4}$', s)
if m == None:
raise forms.ValidationError('郵便番号の型式ではありません!')
m = re.search(r'^[^0-9][a-zA-Z0-9_-]+([.][a-zA-Z0-9_-]+)*[@][a-zA-Z0-9_-]+([.][a-zA-Z0-9_-]+)*[.][a-zA-Z]{2,4}$', s)
if m == None:
raise forms.ValidationError('メールアドレスの型式ではありません!')
return s
● DateField
from datetime import date, time, datetime
def clean_mydate(self):
#日付形式 2018-10-10 で取得できる
s = self.cleaned_data['mydate']
d = date.today()
if s > d:
raise forms.ValidationError('未来はだめ~')
return s
● ChoiceField
def clean_mysex(self):
s = self.cleaned_data['mysex']
if s == '':
raise forms.ValidationError('必須だよ')
elif s == '3':
raise forms.ValidationError('3かよ!')
return s
● BooleanField
def clean_flg(self):
#ブーリアンで取得できる
s = self.cleaned_data['flg']
if s == False:
raise forms.ValidationError('必須だよ')
return s
● MultipleChoiceField
def clean_chk(self):
s = self.cleaned_data['chk']
if len(s) < 2:
raise forms.ValidationError('最低2個は選んで')
return s
● CharField
def clean_area(self):
s = self.cleaned_data['area']
if len(s) > 10:
#改行は2文字とカウントされる
raise forms.ValidationError('10文字までです')
return s
【注意】
self.cleaned_data['hoge'] で値を取得する場合は順番が大切!!
例えば以下の様な定義がある場合、
hoge = forms.CharField()
foo = forms.CharField()
bar = forms.CharField()
このとき、以下の様に foo の独自バリデーションを作成すると bar の値は取得できない
def clean_foo(self):
#bar は取得できない。エラーとなる!!
s = self.cleaned_data['bar']
これは、定義の順番の影響で clean_foo メソッドを処理するタイミングでは cleaned_data にまだ bar の値が入っていないためです。
なので、clean_bar メソッドの処理では逆に cleaned_data に foo の値は格納されています。
面倒な場合は clean メソッドを使う手もあります。
また、注意が必要なのが、バリデーションエラーのため raise した値も取得できません。( clean メソッドでも無理 )
例)
def clean_hoge(self):
s = self.cleaned_data['hoge']
if hoge == '':
raise forms.ValidationError('必須項目です') ※【1】
return s ※【2】
def clean_foo(self):
#処理が【1】の場合はエラーとなる。【2】の場合は正常に値を取得できる
s = self.cleaned_data['hoge']
これを避けるには、クラス変数を別途用意して raise する前にその変数に格納するしかないかも。
forms.py に定義した各フィールドの属性で全て同じものがあれば共通の設定をすればコードが少なくなります
メソッド __init__(self, *args, **kwargs) で設定します
例)
#引数はお約束
def __init__(self, *args, **kwargs):
#HogeForm は、自身のクラス名
super(HogeForm, self).__init__(*args, **kwargs)
for field in self.fields.values():
#style 属性を共通の定義にする
field.widget.attrs['style'] = 'color:red;'
#required 属性を共通の定義にする
field.required = True
基本的にフォームの内容は DB へ保存します。
なので、モデルとフォームを関連付ければ簡単にバリデーションから保存まで完結します。
例)UserTbl クラスと関連付ける
from django import
forms
from .models import UserTbl
#ModelForm クラスを継承する
class UserTblModelForm(
forms.
ModelForm):
class Meta:
#UserTbl クラスと関連付け
model = UserTbl
#全カラムを対象とする
fields = '__all__'
#syumi_lit テーブルから flg 1以外を抽出し、(id , name) タプルの配列を作成する
SYUMI_option = [(s.id,s.name) for s in SyumiList.objects.all().exclude(flg='1')]
#通常の form 通り記述する
name =
forms.CharField(
required = False,
max_length = 20,
widget =
forms.TextInput(
attrs = {'size':'30'}
)
)
syumi =
forms.MultipleChoiceField(
required = False,
widget =
forms.CheckboxSelectMultiple(),
#DB の値を使ったチェックボックスとなる
choices =
SYUMI_option,
)
【メモ】
fields = '__all__' の記述は、モデルに定義しているカラムを全てフォームの対象とする意味です。
なので、フォームに表示していないカラムでも内部的にはバリデーションの対象になっているため、予期せぬバリデーションエラーになるリスクがあります。
これを回避するには fields = '__all__' の代わりに以下の2通りの記述方法があります。
fields = ("name", "age") ※ name と age カラムをバリデーションの対象とする
exclude = ("name", ) ※ name カラムはバリデーションの対象としない