"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

プルダウンリスト(Select)とマルチセレクトボックス。初期化と値の受取など。/Django3

f:id:arakan_no_boku:20190320212948j:plain

 今回は「Select」に対応する部品です。

環境は Windows + anaconda です。

目次

ChoiceFieldと MultipleChoiceField

表示と選択のパターンは以下の4つです。

  1. 固定の選択肢を表示(静的)して、単一選択のみが可能
  2. 固定の選択肢を表示(静的)して、複数選択が可能
  3. 動的に生成した選択肢を表示して、単一選択のみが可能
  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」は指定しません。 

views.pyにおける動的選択肢セット

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に渡すリストに値が複数はいる場合があります。

ちなみに、上記の例だと、初期表示時は全選択になります。 

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 %}

共通テンプレートとの差分だけ載せています。

共通テンプレートについては、こちらを参照ください。

arakan-pgm-ai.hatenablog.com

urls.pyには、以下のように追加して動作確認しました。

path('exp2/', views.demo2, name='exsample2'),

 

実行した画面イメージf:id:arakan_no_boku:20210504185403p:plain

ではでは。