DjangoのChoiceFieldのわかりやすい使い方・書き方

307, 2021-08-18

目次

DjangoのChoiceFieldのわかりやすい使い方

Djangoではフォームを使うことが出来ますが、フォームのフィールドにはChoiceFieldというフィールドを指定することが出来ます。
ChoiceFieldは画面上ではプルダウンメニューで表示されるフィールドです。
複数の選択肢を定義したフィールドを使いたい場合にぴったりなフィールドと言えます。

結論から言うとChoiceFieldを使ったDjangoのフォームは↓のように書きます。

from django import forms
from django.db import models


class FoodChoices(models.TextChoices):
    BREAD = 'bread', 'パン'
    RICE = 'rice', 'ご飯'
    FISH = 'fish', '魚'
    MEAT = 'meat', '肉'


class FoodForm(forms.Form):
    food = forms.fields.ChoiceField(
        choices=FoodChoices.choices,
        required=True,
        label='食べ物',
        # widget=forms.widgets.Select,
    )

この記事ではDjangoのChoiceFieldについて詳細に見ていきます。
具体的には↓になります。

  • ChoiceFieldの定義方法

  • 実際にChoiceFieldを使ってみる

  • ChoiceFieldの構造

関連記事
DjangoのBooleanFieldの使い方: 真偽値, True, False
DjangoのDateTimeFieldの詳しい使い方: 日付と時刻を扱うフィールド

ChoiceFieldの定義方法

ChoiceFielddjango.forms.fieldsに保存されています。
ChoiceFieldを使ったフォームを定義するには↓のようにします。

from django import forms
from django.db import models


class FoodChoices(models.TextChoices):
    BREAD = 'bread', 'パン'
    RICE = 'rice', 'ご飯'
    FISH = 'fish', '魚'
    MEAT = 'meat', '肉'


class FoodForm(forms.Form):
    food = forms.fields.ChoiceField(
        choices=FoodChoices.choices,
        required=True,
        label='食べ物',
        # widget=forms.widgets.Select,
    )

↑の場合、FoodFormというのがChoiceFieldを使っているフォームです。
このフォームの属性foodChoiceFieldです。
ChoiceFieldの引数にはchoices, required, labelなどを指定します。
ChoiceFieldはデフォルトではforms.widgets.Selectをウィジェットとして使います。

引数choicesに選択肢の定義を渡します。
選択肢の定義はFoodChoicesというクラスで行っています。
FoodChoicesdjango.db.models.TextChoicesを継承したクラスです。
TextChoicesは↑のように定数名(BREADRICEなど)に対して「値, 表示ラベル」というふうにタプルで定義します。
この表示ラベルがフォームのプルダウンで表示されて、値が実際に選択された場合の値になります。

選択肢の定数の参照方法

選択肢(FoodChoices)の定数は参照することが可能です。
具体的には↓になります。

  • 「定数.name」で定数の名前を参照

  • 「定数.value」で定数の値を参照

  • 「定数.label」で定数のラベルを参照

たとえば↓のようなビューを定義します。

from myapp.forms import FoodChoices
from django.http import HttpResponse


def enum_view(request):
    html = f'{FoodChoices.BREAD.name}: {FoodChoices.BREAD.value}, {FoodChoices.BREAD.label}<br />'
    html += f'{FoodChoices.RICE.name}: {FoodChoices.RICE.value}, {FoodChoices.RICE.label}<br />'
    return HttpResponse(html)

↑の場合、FoodChoicesforms.pyからインポートしています。
そしてFoodChoices.BREADとやってパンの定数を参照しています。
FoodChoices.BREAD.nameで定数の名前、そしてFoodChoices.BREAD.valueで定数の値を参照します。さらにFoodChoices.BREAD.labelでラベルを参照します。

↑のビューを実行すると↓のような出力になります。

BREAD: bread, パン
RICE: rice, ご飯

旧来の定義方法

さきほどのTextChoicesを使った定義方法は比較的に新しい定義方法になります。
旧来の方法は↓のようになります。

class OldFoodForm(forms.Form):
    FOOD_CHOICES = (
        ('bread', 'パン'),
        ('rice', 'ご飯'),
        ('fish', '魚'),
        ('meat', '肉'),
    )

    food = forms.fields.ChoiceField(
        choices=FOOD_CHOICES,
        required=True,
        label='古い食べ物',
    )

↑のように選択肢を定義したFOOD_CHOICESというタプルを定義します。
タプルの中にタプルで「値, 表示テキスト」という順番に書きます。
こうすることで選択肢が機能します。

新しい方法と比べると、旧来の方法は定数を参照したい場合にタプルのインデックスで指定しなければいけません。
新しい方では定数名で参照できるので、利便性では新しい方法のほうが高いと言えそうです。

実際にChoiceFieldを使ってみる

実際にChoiceFieldを定義したフォームを使ってみます。
まずurls.pyを定義します。

from django.contrib import admin
from django.urls import path
from myapp.views import home_view


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home_view),
]

そしてビューを定義します。

from django.shortcuts import render
from django.http import HttpResponse
from myapp.forms import FoodForm


def home_view(request):
    if request.method == 'GET':
        return render(request, 'myapp/home.html', {
            'form': FoodForm(),
        })
    elif request.method == 'POST':
        form = FoodForm(request.POST)
        if not form.is_valid():
            return render(request, 'myapp/home.html', {
                'form': form,
            })

        food = form.cleaned_data['food']
        return HttpResponse(food)

テンプレートファイルは↓です。

<form action="/" method="POST">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="送信" />
</form>

ブラウザで/にアクセスすると↓のように表示されます。

home.png

そして選択肢を選択して送信ボタンをクリックすると、画面にはその選択肢の値が表示されます。

ChoiceFieldの構造

ChoiceFielddjango.forms.fields内に定義されています。
ChoiceFieldFieldを継承したクラスです。
__init__()メソッドの構造は↓になります。

def __init__(self, *, choices=(), **kwargs):
    ...

__init__()ではkwargsを親の__init__()に渡しています。
そのためchoicesを除いた引数の扱いはほとんどFieldの仕事になっています。

Fielddjango.forms.fields内で定義されています。
Field__init__()は↓のような構造になっています。

def __init__(self, *, required=True, widget=None, label=None, initial=None,
                 help_text='', error_messages=None, show_hidden_initial=False,
                 validators=(), localize=False, disabled=False, label_suffix=None):
    ...

おわりに

今回はDjangoのChoiceFieldについて見てみました。
選択肢を扱いたい場合にChoiceFieldは非常に便利なフィールドです。

選択肢は無限にある

選ぶのも一苦労