目次
ChoiceFieldと MultipleChoiceField:選択肢のセット
今回は「Select」に対応する部品です。
環境は Windows + anaconda です。
表示と選択のパターンは以下の4つです。
- 固定の選択肢を表示(静的)して、単一選択のみが可能
- 固定の選択肢を表示(静的)して、複数選択が可能
- 動的に生成した選択肢を表示して、単一選択のみが可能
- 動的に生成した選択肢を表示して、複数選択が可能
実際に使うのは、ほとんど「3」か「4」だと思います。
DBやファイルから選択肢を取得して、選択肢を生成するパターンですね。
Djangoだと、ModelChoiceFieldを使った例が多いですが、今回は使いません。
依存関係を疎にするため、画面(View)からDBに直接アクセスしないルールでやりたいので、DBあるなしでFieldを使い分けるメリットを個人的には感じません。
その前提で、上記4パターンのサンプルです。
サンプル1:固定の選択肢を表示(静的)して、単一選択のみが可能
labels = ['静的単一選択','静的複数選択','動的単一選択','動的複数選択'] CHOICE = [ ('1','選択肢<1>'), ('2','選択肢<2>'), ('3','選択肢<3>')] one = forms.ChoiceField( label=labels[0], required=True, disabled=False, initial=['2'], choices=CHOICE, widget=forms.Select(attrs={ 'id': 'one',}))
選択肢「choices」に渡すのは、(キー,バリュー)の組合せのタプルのリストです。
上記だと「CHOICE」です。
初期表示時に選択済「selected」にするキーは「initial」に渡します。
こちらはlist。
上記の例なら、初期表示時に「 ('2','選択肢<2>')」が選択済になるというわけです。
サンプル2:固定の選択肢を表示(静的)して、複数選択が可能
labels = ['静的単一選択','静的複数選択','動的単一選択','動的複数選択'] CHOICE = [ ('1','選択肢<1>'), ('2','選択肢<2>'), ('3','選択肢<3>')] two = forms.MultipleChoiceField( label=labels[1], required=True, disabled=False, initial=['2'], choices=CHOICE, widget=forms. SelectMultiple(attrs={ 'id': 'two',}))
MultipleChoiceFieldとSelectMultipleが違うだけで方法は前のサンプルと同じです。
サンプル3:動的に生成した選択肢を表示して、単一選択のみが可能
このパターンは、views.pyで動的に、「choices 」と「initial」をセットします。
labels = ['静的単一選択','静的複数選択','動的単一選択','動的複数選択'] three = forms.ChoiceField( label=labels[2], required=True, disabled=False, widget=forms.Select(attrs={ 'id': 'three',}))
なので、forms.pyの定義で「choices 」と「initial」は指定しません。
サンプル4:動的に生成した選択肢を表示して、複数選択が可能
このパターンも、views.pyで動的に、「choices 」と「initial」をセットします。
labels = ['静的単一選択','静的複数選択','動的単一選択','動的複数選択'] four = forms.MultipleChoiceField( label=labels[3], required=True, disabled=False, widget=forms. SelectMultiple(attrs={ 'id': 'four',}))
forms.pyの定義で、これも、「choices 」と「initial」は指定しません。
forms.pyの中で選択肢をセットする場合は、views.pyの中で特にすることはないのですけど、上記の後の2つのように「choices」と「initial」を指定していない場合は、views.pyでセットする必要があります。
動的に選択肢をセットするサンプル1:単一選択の場合
form = forms.SmplForm() choice1 = [] choice1.append(('1','選択肢(1)')) choice1.append(('2','選択肢(2)')) choice1.append(('3','選択肢(3)')) choice1.append(('4','選択肢(4)')) form.fields['three'].choices = choice1 form.fields['three'].initial = ['3']
単純にタプルのリストを生成して、choicesにセットし、キー値をひとつだけ収めたlistを生成して、initialにセットする 。
ただ、それだけです。
DBからデータを取得した場合は、Selectした値をタプルにして、listに追加してやれば同じようにできると考えれば、まあ、わかりやすいです。
ただ、form内のフィールドを取得する方法が、意外と情報がないので、忘れてしまうと、ちょっとてこずりますけどね。
動的に選択肢をセットするサンプル2:複数選択の場合
form = forms.SmplForm() 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 = ['1','2','3','4']
複数選択の場合もchoicesは同じです。
ただ、複数選択の場合は、初期表示時に複数が「selected」になる場合があるので、initialに渡すリストに値が複数はいる場合があります。
ちなみに、上記の例だと、初期表示時は全選択になります。
値の受取
選択された値をうけとる方法です。
- 単一選択
- 複数選択
の2パターンがあります。
views.pyの選択値を受け取る1:単一選択
Submitボタンを押して、request.POSTで選択肢を受け取る想定です。
例えば、forms.pyで「one」という名前で定義したFieldで選択した値は
request.POST["one"]
で受け取ります。
views.pyの選択値を受け取る2:複数選択
上記だと、複数選択した場合でも最後のひとつしか受け取れません。
なので、複数選択する可能性のあるFieldの値は
request.POST.getlist("two")
のように、POST.getlist()をつかって取得します。
いちおう、こんなもんですかね。
サンプルソース全体
最後に、サンプルのソース全体と実行した時の画面イメージをのせておきます。
サンプルのソース:forms.py
from django import forms class SmplForm(forms.Form): labels = ['静的単一選択','静的複数選択','動的単一選択','動的複数選択'] CHOICE = [ ('1','選択肢<1>'), ('2','選択肢<2>'), ('3','選択肢<3>')] one = forms.ChoiceField( label=labels[0], required=True, disabled=False, initial=['2'], choices=CHOICE, widget=forms.Select(attrs={ 'id': 'one',})) two = forms.MultipleChoiceField( label=labels[1], required=True, disabled=False, initial=['2'], choices=CHOICE, widget=forms. SelectMultiple(attrs={ 'id': 'two',})) three = forms.ChoiceField( label=labels[2], required=True, disabled=False, widget=forms.Select(attrs={ 'id': 'three',})) four = forms.MultipleChoiceField( label=labels[3], required=True, disabled=False, widget=forms. SelectMultiple(attrs={ 'id': 'four',}))
--
サンプルのソース: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 demo2(request): # 入力結果を格納する辞書 results = {} ret = '' if request.method == 'POST': # 入力されたデータの受取 results['one'] = request.POST["one"] results['two'] = request.POST.getlist("two") results['three'] = request.POST["three"] listret = request.POST.getlist("four") print(listret) ret = 'OK' c = {'results': results,'ret':ret,'listret':listret} else: form = forms.SmplForm() choice1 = [] choice1.append(('1','選択肢(1)')) choice1.append(('2','選択肢(2)')) choice1.append(('3','選択肢(3)')) choice1.append(('4','選択肢(4)')) form.fields['three'].choices = choice1 form.fields['three'].initial = ['3'] form.fields['four'].choices = choice1 form.fields['four'].initial = ['1','2','3','4'] c = {'form': form,} # CFRF対策(必須) c.update(csrf(request)) return render(request,'demo03.html',c)
--
サンプルのソース:表示用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> <h2>{{ key }} : {{ val }}</h2> </td> </tr> {% endfor %} {% for field in form %} <tr> <td><h4>{{ field.label_tag }}</h4></td> <td>{{ field|add_class:"form-control font-weight-bold" }}</td> </tr> {% endfor %} <tr> <td colspan="2"> <button type="submit" class="btn btn-primary">送信する</button> </td> </tr> {% endif %} </form> {% endblock %}
共通テンプレートとの差分だけ載せています。
共通テンプレートについては、こちらを参照ください。
サンプル実行画面イメージ
urls.pyには、以下のように追加して動作確認しました。
path('exp2/', views.demo2, name='exsample2'),
実行した画面イメージ
ではでは。