python3.doc_50
R e i - D r e a m
for Laravel
TOP > Python3 > Django(FROMクラス)
Guest
login

最終投稿日:2022年6月18日

Django(FROMクラス)
formクラス基本
フォームに必要な各部品やバリデーションの定義をします。
特に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 カラムはバリデーションの対象としない
ログインしてコメントを残そう!!


きっぷる