目次
チェックボックスとRadioボタン
今回は「CheckBox」と「RadioButton」です。
環境は Windows + anaconda です。
3パターンのFieldを使い分ける方法がよく紹介されてます。
- BooleanFieldで単一のチェックボックス。
- RadioボタンはChoiceFieldで、RadioSelectを使う。
- チェックボックスの複数選択の場合はMultipleChoiceFieldを使う。
などです。
ただ、使い分けるのが面倒くさいので、自分はチェックボックスも、Radioボタンも、チェックボックスの複数選択もまとめて「MultipleChoiceField」一択でやってます。
チェックボックスとラジオボタンを使うパターンとしては5つあります。
- 複数選択可能なチェックボックス(選択肢は静的)
- 単一のチェックボックス
- 複数選択可能なチェックボックス(選択肢は動的)
- ラジオボタン(Radio)(選択肢は静的)
- ラジオボタン(Radio)(選択肢は動的)
それぞれの定義の仕方です。
forms.pyに定義します。
サンプルパターン1:単一のチェックボックス
かなりの力技なんですが。
class ChkForm(forms.Form): labels = ['チェック','複数チェック','ラジオボタン','動的選択肢1','動的選択肢2'] SINGLE_CHOICE = [('1','OKなららチェック')] one = forms.MultipleChoiceField( label=labels[0], required=False, disabled=False, initial=[], choices=SINGLE_CHOICE, widget=forms.CheckboxSelectMultiple(attrs={ 'id': 'one','class': 'form-check-input'}))
複数選択と同じだけど、選択肢をひとつだけ渡せば「単一チェックだよ」という理屈。
値を受け取る側のロジックをシンプルにできるメリットがあります。
サンプルパターン2:複数選択可能なチェックボックス(選択肢は静的)
class ChkForm(forms.Form): labels = ['チェック','複数チェック','ラジオボタン','動的選択肢1','動的選択肢2'] CHOICE = [ ('1','選択肢<1>'), ('2','選択肢<2>'), ('3','選択肢<3>')] two = forms.MultipleChoiceField( label=labels[1], required=False, disabled=False, initial=[], choices=CHOICE, widget=forms.CheckboxSelectMultiple(attrs={ 'id': 'two','class': 'form-check-input'}))
Selectと同じで「キー、バリュー」のタプルのりすとを、choicesに渡して選択肢を設定します。
initial=[]なら、すべて未選択状態でチェックボックスを表示します。
CSSはBootstrap4を使う前提なので、「'class': 'form-check-input'」の指定が必要です。
これを忘れると、きれいに表示されません。
サンプルパターン3:複数選択可能なチェックボックス(選択肢は動的)
class ChkForm(forms.Form): labels = ['チェック','複数チェック','ラジオボタン','動的選択肢1','動的選択肢2'] four = forms.MultipleChoiceField( label=labels[3], required=False, disabled=False, widget=forms.CheckboxSelectMultiple(attrs={ 'id': 'four','class': 'form-check-input'}))
上記の2つとの違いは、initialとchoicesがないこと。
選択肢と初期状態(選択・非選択)は、views.pyの中で実行時に渡します。
DBから値をとってきてセットするイメージです。
サンプルパターン4:ラジオボタン(Radio)(選択肢は静的)
class ChkForm(forms.Form): labels = ['チェック','複数チェック','ラジオボタン','動的選択肢1','動的選択肢2'] CHOICE = [ ('1','選択肢<1>'), ('2','選択肢<2>'), ('3','選択肢<3>')] three = forms.MultipleChoiceField( label=labels[2], required=False, disabled=False, initial=['2'], choices=CHOICE, widget=forms.RadioSelect(attrs={ 'id': 'three','class': 'form-check-input'}))
チェックボックスとの違いは、RadioSelectを使うところだけです。
サンプルパターン5:ラジオボタン(Radio)(選択肢は動的)
class ChkForm(forms.Form): labels = ['チェック','複数チェック','ラジオボタン','動的選択肢1','動的選択肢2'] five = forms.MultipleChoiceField( label=labels[4], required=False, disabled=False, widget=forms.RadioSelect(attrs={ 'id': 'five','class': 'form-check-input'}))
なので。
動的に選択肢と初期状態(選択)を渡すパターンは、choicesとinitialがないだけです。
チェックボックスとRadioボタンに動的に選択肢等を渡すサンプル
上記を利用して簡単な画面を作り、動的に生成した選択肢を私、選択された値を取得するサンプルをかいてみます。
まずは、ソースコードから。
views.pyです。
from django.http.response import HttpResponse from django.shortcuts import render, render_to_response from django.contrib.staticfiles.templatetags.staticfiles import static from . import forms from django.template.context_processors import csrf def demo3(request): labels = ['チェック','複数チェック','ラジオボタン','動的選択肢1','動的選択肢2'] # 入力結果を格納する辞書 results = {} radios = {} ret = '' if request.method == 'POST': # 入力されたデータの受取 results[labels[0]] = request.POST.getlist("one") results[labels[1]] = request.POST.getlist("two") results[labels[2]] = request.POST.getlist("three") results[labels[3]] = request.POST.getlist("four") results[labels[4]] = request.POST.getlist("five") ret = 'OK' c = {'results': results,'ret':ret} else: form = forms.ChkForm() choice1 = [] choice1.append(('1','動的選択肢1')) choice1.append(('2','動的選択肢2')) choice1.append(('3','動的選択肢3')) choice1.append(('4','動的選択肢4')) form.fields['four'].choices = choice1 form.fields['four'].initial = ['2'] form.fields['five'].choices = choice1 form.fields['five'].initial = ['3'] c = {'form': form,'ret':ret} # CFRF対策(必須) c.update(csrf(request)) return render(request,'demo03.html',c)
ポイントを補足します。
サンプルのポイント1:動的に選択肢と初期状態を渡す
この部分です。
form = forms.ChkForm()
choice1 =
choice1.append(('1','動的選択肢1'))
choice1.append(('2','動的選択肢2'))
choice1.append(('3','動的選択肢3'))
choice1.append(('4','動的選択肢4'))
form.fields['four'].choices = choice1
form.fields['four'].initial = ['2']
form.fields['five'].choices = choice1
選択肢のリストを生成して、choicesとinitialにセットしてます。
initial=['2']だと、2番目の「動的選択肢2」にチェックだけにチェックがつきます。
サンプルのポイント2:選択された値の受取
この部分です。
results[labels[0]] = request.POST.getlist("one")
results[labels[1]] = request.POST.getlist("two")
results[labels[2]] = request.POST.getlist("three")
results[labels[3]] = request.POST.getlist("four")
results[labels[4]] = request.POST.getlist("five")
基本、Multipleなので、全部getlistで受け取るわけです。
チェックが一つもついてなければ(空のリスト}が返ります。
チェックがついていれば、そのキーが返ります。
['1'] とか、['2','3']みたいな感じで。
なので、単一チェックでも複数チェックでもラジオボタンでも、みんな同じロジックで処理ができるわけで・・これが、自分がこのやり方を気に入っている最大の理由です。
サンプルのポイント3:MultipleChoiceFieldを使う場合の注意点
DjangoのMultipleChoiceFieldは、難儀な癖があります。
そのまま使うと<ul>~</ul>で囲まれてしまうのです。
本家のドキュメントから引用します。
Widgets | Django documentation | Django
Selectと似ていますが、<li>タグ内のラジオボタンのリストとしてレンダリングされます。
<ul>
<li><input type='radio' name='...'></li>
...
</ul>
これはレイアウトの自由度が制限されてしまうので、実に具合が悪いです。
なので、HTML側で自分でレイアウトしてやる必要があります。
条件分岐に必要な「inputのtype名」を取得する方法はこうです。
field.field.widget.input_type == "radio"
チェックボックスなら。
field.field.widget.input_type == "checkbox"
type名識別を考慮したHTMLを書くと、以下のようになります。
% for field in form %} {% if field.field.widget.input_type == "radio" %} <tr> <td>{{ field.label_tag }}</td> <td> {% for r in field %} {{ r }}</br> {% endfor %} </td> </tr> {% else %} <tr> <td>{{ field.label_tag }}</td> <td> {% for c in field %} {{ c }}</br> {% endfor %} </td> </tr> {% endif %} {% endfor %}
上記では改行で縦並びにしています。
改行をとると横並びになります。
サンプルを実行した画面のイメージ
初期表示(縦並びパターン)から。
これでチェックボックスを複数選んだりして、送信ボタンをおします。
まあ、こんな感じで値を受け取れるということですね。
参考:ソース全文
最後にソース全文をのせておきます。
ソース:forms.py
from django import forms class ChkForm(forms.Form): labels = ['チェック','複数チェック','ラジオボタン','動的選択肢1','動的選択肢2'] SINGLE_CHOICE = [('1','OKなららチェック')] CHOICE = [ ('1','選択肢<1>'), ('2','選択肢<2>'), ('3','選択肢<3>')] one = forms.MultipleChoiceField( label=labels[0], required=False, disabled=False, initial=[], choices=SINGLE_CHOICE, widget=forms.CheckboxSelectMultiple(attrs={ 'id': 'one','class': 'form-check-input'})) two = forms.MultipleChoiceField( label=labels[1], required=False, disabled=False, initial=[], choices=CHOICE, widget=forms.CheckboxSelectMultiple(attrs={ 'id': 'two','class': 'form-check-input'})) four = forms.MultipleChoiceField( label=labels[3], required=False, disabled=False, widget=forms.CheckboxSelectMultiple(attrs={ 'id': 'four','class': 'form-check-input'})) three = forms.MultipleChoiceField( label=labels[2], required=False, disabled=False, initial=['2'], choices=CHOICE, widget=forms.RadioSelect(attrs={ 'id': 'three','class': 'form-check-input'})) five = forms.MultipleChoiceField( label=labels[4], required=False, disabled=False, widget=forms.RadioSelect(attrs={ 'id': 'five','class': 'form-check-input'}))
ソース:views.py
from django.http.response import HttpResponse from django.shortcuts import render, render_to_response from django.contrib.staticfiles.templatetags.staticfiles import static from . import forms from django.template.context_processors import csrf def demo3(request): labels = ['チェック','複数チェック','ラジオボタン','動的選択肢1','動的選択肢2'] # 入力結果を格納する辞書 results = {} radios = {} ret = '' if request.method == 'POST': # 入力されたデータの受取 results[labels[0]] = request.POST.getlist("one") results[labels[1]] = request.POST.getlist("two") results[labels[2]] = request.POST.getlist("three") results[labels[3]] = request.POST.getlist("four") results[labels[4]] = request.POST.getlist("five") ret = 'OK' c = {'results': results,'ret':ret} else: form = forms.ChkForm() choice1 = [] choice1.append(('1','動的選択肢1')) choice1.append(('2','動的選択肢2')) choice1.append(('3','動的選択肢3')) choice1.append(('4','動的選択肢4')) form.fields['four'].choices = choice1 form.fields['four'].initial = ['2'] form.fields['five'].choices = choice1 form.fields['five'].initial = ['3'] c = {'form': form,'ret':ret} # CFRF対策(必須) c.update(csrf(request)) return render(request,'demo03.html',c)
ソース:HTML(exsample3.html)
{% extends 'base.html' %} {% load static %} {% load widget_tweaks %} {% block header %} <meta name="dummy" />{ % endblock %} {% block title %} やあ。こんにちは。 {% endblock %} {% block content %} <tr> <td colspan="2"><img src="{% static "images/k.png" %}" /></td> </tr> <form action="" method="post">{% csrf_token %} <h1>ビルトインフォームサンプル</h1> {% if ret %} {% for key,val in results.items %} <tr> <td><h4>{{ key }}</h4></td> <td><h4>{{ val }}</h4></td> </tr> {% endfor %} {% else %} {% for key,val in errors.items %} <tr> <td colspan="2">{{ key }} : {{ val }}</td> </tr> {% endfor %} {% for field in form %} {% if field.field.widget.input_type == "radio" %} <tr> <td>{{ field.label_tag }}</td> <td> {% for r in field %} {{ r }}</br> {% endfor %} </td> </tr> {% else %} <tr> <td>{{ field.label_tag }}</td> <td> {% for c in field %} {{ c }}</br> {% endfor %} </td> </tr> {% endif %} {% endfor %} <tr> <td colspan="2"> <button type="submit" class="btn btn-primary">送信する</button> </td> </tr> {% endif %} </form> {% endblock %}
HTMLは共通テンプレートを使う前提の差分のみです。
ではでは。