Formクラスのモジュール

基本的な使い方

forms.pyでの定義

Djangoが用意しているフォーム関連の親クラスを継承して、カスタムフォームクラスの定義をする。Djangoの基本的なフォームクラスはdjango.forms.Formとなっている。

アプリのDIR直下にforms.pyというファイルを作る。例えばdjango_appプロジェクトのhelloアプリならば、django_app/hello/forms.py。その先頭でfrom django import formsとしてDjangoの基本的なフォームクラスをインポートし、作りたいカスタムフォームクラスの引数にforms.Formを渡す。

#forms.py
from django import forms

class HelloForm(forms.Form):
  name = forms.CharField(label='name', widget=forms.TextInput(attrs={'class':'form-control'}))
  mail = forms.CharField(label='mail', widget=forms.TextInput(attrs={'class':'form-control'}))
  age = forms.IntegerField(label="age", widget=forms.NumberInput(attrs={'class':'form-control'}))
  ..........

作ったクラスの中で、フィールド変数を変数名 = forms.フィールドクラス(フィールドの引数)という形で定義する。

フィールド引数

  • required: True/False、値はbool型で文字列では無いので注意、またPythonなので先頭大文字
  • label: 入力フィールドに付けられるラベルの文字列
  • initial: 初期値
  • widget: widget を指定する。
  • help_text: 文字列
  • error_messages: エラーメッセージをディクショナリで渡* す {'required':'Enter your name.'} など
  • validators (Django 1.2 以降)
  • localize (Django 1.2 以降)

例:「体重」をインプットする実数値の入力フィールドをこのように定義すると、

body_weight = forms.FloatField(label="体重", initial="45", widget=forms.NumberInput(attrs={'class':'body-weight'}))

HTMLはこうなる。

出力されるHTML

<p>
  <label for="id_body_weight">体重:</label> 
  <input type="number" name="body_weight" value="45" class="body-weight" step="any" required="" id="id_body_weight">
</p>
出力タグ
<input type="number">
ラベル
「体重」
ラベルのID
id_フィールド変数名
初期値
45
inputタグの属性
name="フィールド変数名",class="body-weight",IDがid_フィールド変数名,requiredの属性があるので入力が必須。

widget フィールド引数

フォームフィールドの外見を決めるもので、HTMLタグにクラス名を渡すなどに利用できるが、例えば<input type="number">などにしたいのにforms.TextInputといった値にしていたのではそうならないので注意。

widgetの種類とレンダリングされるタグの関係

TextInput : <input type="text" ...>
NumberInput : <input type="number" ...>
EmailInput : <input type="email" ...>
URLInput : <input type="url" ...>
PasswordInput : <input type="password" ...>
HiddenInput : <input type="hidden" ...>
DateInput : <input type="text" ...>
CheckBoxInput : <input type="checkbox" ...>
Select : <select><option ...><select>

views.pyでの利用

後はそのフォームを使いたいビューの中で、つまりアプリのviews.pyの中でインポートし、TemplateViewクラスを継承したクラスの中で、初期化時のパラメータに代入したうえでメソッドで利用する。

# views.py
from .forms import HelloForm

class HelloView(TemplateView):

  def __init__(self):
    self.params = {
      ......
      'form': HelloForm()
    }
  
  ..........

  def post(self, request):
    ........
    self.params['form'] = HelloForm(request.POST)

テンプレートでの利用

テンプレートの中で{{ form }}と書いた箇所に自動的にフォームが出力される。この場合forms.pyのカスタムフォームクラスに定義した順番に出力される。実際forms.pyで上記のage = forms.IntegerField(....)を最初に持ってくると、HTMLでは年齢入力フィールドが先頭になる。

例:hello/templates/hello/index.htmlでカスタムフォームクラスのインスタンスを使う。

<form action="{% url 'index' %}" method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <input type="submit" value="click" class="btn btn-primary my-2">
</form>

ビルトインFieldクラス

CharField

文字列のフィールド=テキスト入力のためのクラス。 <input type="text">を生成する。

EmailField

メールアドレスの入力フィールド。 <input type="email">を生成。メールアドレス形式では無い文字列を入力すると「Enter a valid email address.」といったエラーメッセージが出る。

IntegerField

整数値の入力フィールド。 <input type="number">を生成。 対応しているブラウザならば、右端に増減ボタンなどが表示される。整数値以外の入力には「有効な値を入力して下さい」などの警告が出る。

FloatField

実数値も入力できるフィールド。 <input type="number">を生成。

URLField

URL入力のためのフィールド。 <input type="url">を生成。入力されたアドレスが実際に存在するかまでのチェックはできないが、URL文字列になっているかのチェックは行う。「Enter a valid URL.」などの警告が出る。

日時に関するフィールド

DateField

日付の入力フィールド。

TimeField

時刻の入力フィールド

DateTimeField

日付と時間の入力フィールド

入力できる日時のフォーマットは制限がある。

  • 日時の形式
    2020-02-01
    02/01/2020
    02/01/20

  • 時刻の形式
    12:34
    12:34:45

  • 日付と時間:日付と時間をスペース区切りで入力
    02/01/2020 12:34

BooleanField

チェックボックスのフィールド

チェックボックスはOFFの場合値が送信されないので、必ずrequired=Falseにする。

check = forms.BooleanField(label='CheckBox', required=False)

hello/forms.py

class HelloForm(forms.Form):
  check = forms.BooleanField(label='CheckBox', required=False)

hello/views.py

class HelloView(TemplateView):

  def __init__(self):
    self.params = {
      'title': 'Hello',
      'form': HelloForm(),
      'checked': False,
    }

  def get(self, request):
    return render(request, 'hello/index.html', self.params)

  def post(self, request):
    if ('check' in request.POST):
      self.params['checked'] = True
    else:
      self.params['checked'] = False
    self.params['form'] = HelloForm(request.POST)
    return render(request, 'hello/index.html', self.params)

hello/templates/hello/index.html(body部分のみ)

<body class="container">
  <h1 class="display-4 text-primary">{{title}}</h1>
  <p class="h5 mt-4">{{message|safe}}</p>
  {% if checked %}
  <p class="h5 mt-4">Checked!</p>
  {% else %}
  <p class="h5 mt-4">No check.</p>
  {% endif %}
  <form action="{% url 'index' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="click" class="btn btn-primary my-2">
  </form>
</body>

これでチェックボックスがチェックされていれば「Checked!」、されていなければ「No check.」と表示される。

NullBooleanField

'Yes, No, Unknown'の3択フィールド

hello/forms.py

class HelloForm(forms.Form):
  check = forms.NullBooleanField(label='Check')

hello/views.py

class HelloView(TemplateView):

  def __init__(self):
    self.params = {
      'title': 'Hello',
      'form': HelloForm(),
      'result': None,
    }

  def get(self, request):
    return render(request, 'hello/index.html', self.params)

  def post(self, request):
    chk = request.POST['check']
    self.params['result'] = f"You selected: '{chk}'."
    self.params['form'] = HelloForm(request.POST)
    return render(request, 'hello/index.html', self.params)

hello/templates/hello/index.html(body部分のみ)

<body class="container">
  <h1 class="display-4 text-primary">{{title}}</h1>
  <p class="h5 mt-4">{{result|safe}}</p>
  <form action="{% url 'index' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="click" class="btn btn-primary my-2">
  </form>
</body>

'Yes, No, Unknown'の3択プルダウンメニューが表示され、You selected: '....'の部分にunknowntruefalseのいずれかが表示される(Chromeの場合)。

ChoiceField

プルダウンメニュー(選択のフィールド)。choicesという引数を持ち、それにタプルのリストを渡すとプルダウンメニューが表示される。

hello/forms.py

class HelloForm(forms.Form):
  data = [
    ('one', 'item 1'),
    ('two', 'item 2'),
    ('three', 'item 3')
  ]
  choice = forms.ChoiceField(label='Choice', choices=data)

hello/views.py

class HelloView(TemplateView):

  def __init__(self):
    self.params = {
      'title': 'Hello',
      'form': HelloForm(),
      'result': None,
    }

  def get(self, request):
    return render(request, 'hello/index.html', self.params)

  def post(self, request):
    ch = request.POST['choice']
    self.params['result'] = f'You selected: "{ch}".'
    self.params['form'] = HelloForm(request.POST)
    return render(request, 'hello/index.html', self.params)

hello/templates/hello/index.html(body部分のみ)

<body class="container">
  <h1 class="display-4 text-primary">{{title}}</h1>
  <p class="h5 mt-4">{{result|safe}}</p>
  <form action="{% url 'index' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="click" class="btn btn-primary my-2">
  </form>
</body>

これでhttp://localhost:8000/hello/にアクセスすると「item 1, item 2, item 3」という項目のプルダウンメニューが表示される。生成されるセレクトメニューのリストは、下記のようにタプルで定義した1番目の値が<option value="one">などの形でタグの値になり、2番目の値がメニューに表示される文字列になる。

<p>
  <label for="id_choice">Choice:</label>
  <select name="choice" id="id_choice">
    <option value="one">item 1</option>
    <option value="two">item 2</option>
    <option value="three" selected="">item 3</option>
  </select>
</p>

選んだ項目でch = request.POST['choice']に代入されるのはoptionタグの値=valueになるので、'one','two','three'という値になる。

ラジオボタン

Djangoにはラジオボタン専用のフィールドクラスは無いので、ChoiceField を使って作成する。

forms..ChoiceField(label='radio', choices=データ, widget=forms.RadioSelect())

hello/forms.py

class HelloForm(forms.Form):
  data = [
    ('one', 'radio 1'),
    ('two', 'radio 2'),
    ('three', 'radio 3')
  ]
  choice = forms..ChoiceField(label='radio', choices=data, widget=forms.RadioSelect())

widgetforms.RadioSelectを与えることで、ChoiceFieldを使いながらラジオボタンの表示になる。 他の基本的な仕組みはプルダウンメニューと同じ。

項目選択リスト(一項目選択)

これもやはり一項目選択リスト専用のフィールドクラスは無いので、ChoiceField を使って作成する。

widgetforms.Selectを与えることで、ChoiceFieldを使いながら選択リストの表示になる。

forms..ChoiceField(label='MenuList', choices=データ, widget=forms.Select())

hello/forms.py

class HelloForm(forms.Form):
  data = [
    ('one', 'item 1'),
    ('two', 'item 2'),
    ('three', 'item 3'),
    ('four', 'item 4'),
    ('five', 'item 5'),
  ]
  choice = forms.ChoiceField(label='MenuList', choices=data, widget=forms.Select(attrs={'size':5}))

項目数の属性size="5"をselectタグに渡すために、forms.Selectattrs={'size':5}という引数を与えて表示範囲を決めている。

MultipleChoiceField

複数選択可能な項目選択リストの場合は、MultipleChoiceFieldという専用のフィールドクラスがある。

forms.MultipleChoiceField(label='MenuList', choices=データ, widget=forms.SelectMultiple())

複数選択可能なリストの場合は、POSTで送られてくる値もリストになっている。そのためrequest.POST['choice']といったやり方では無く、request.POST.getlist('choice')という形でgetlistメソッドを使う形にhello/views.pyを書き換える必要がある。 getlistメソッドは送られてきた値をリストとして取り出す。

hello/views.py

class HelloView(TemplateView):

  def __init__(self):
    self.params = {
      'title': 'Hello',
      'form': HelloForm(),
      'result': None,
    }

  def get(self, request):
    return render(request, 'hello/index.html', self.params)

  def post(self, request):
    ch = request.POST.getlist('choice')
    self.params['result'] = f'You selected: "{str(ch)}".'
    self.params['form'] = HelloForm(request.POST)
    return render(request, 'hello/index.html', self.params)

You selected: "['one', 'three', 'four']".

結果表示もYou selected: "['one', 'three', 'four']".というふうにリストで表示する形になる。