Help us understand the problem. What is going on with this article?

[Django] Class Based View を使った入力フォームの作り方

More than 1 year has passed since last update.

この記事について

Djangoにはモデル定義から動的に画面(HTML)を生成するClass Based Viewという仕組みがあります。

Class Based View で入力フォームを出力すると、モデルのフィールドに対応するバリデーション機能とエラー表示(日本語対応)が暗黙的に追加されます。さらに表示をBootstrapに対応させる追加パッケージ Django-Crispy-Forms を組み合わせることでフォーム作成の作業が大幅に簡略化されます。

他のフレームワークのスキャフォールディングに近い役割ですが、オプション設定を行うフォームクラスを追加して細かい調節ができるので自由度は高いです。

この記事では、入力フォームの作り方の要点をまとめました。
本当に要点のみなので、詳しい点についてはリンク先の参考サイトを見てください。

サンプルイメージ

models.py
class Item(models.Model):

    name = models.CharField(max_length=50, verbose_name="名前")
    age = models.IntegerField(verbose_name="年齢")
    memo = models.TextField(max_length=200, verbose_name="備考")
    is_checked = models.BooleanField(verbose_name="確認済み")
    check_date = models.DateField(verbose_name="確認日")

上記のコードで以下の赤線部分が生成される。
バリデーションとエラーメッセージは日付フィールドに合わせて自動的に設定されたものが使われる。

image.png

作成方法

1.Modelを定義する

1-1.フィールド

まずDjangoの一般的な方法で、models.pyにModelを定義しフィールドを追加します。
フィールドの型に合わせて出力されるHTMLのinputタグのtype属性が決まります。

フィールドの種類の例

扱う型 フィールド input type
文字列 CharField text
長い文字列 TextField textareaタグ
数値 IntegerField number
ブール値 BooleanField checkbox

Djangoにはこの他にも大量のフィールドクラスが用意されています。
フィールドによってはmin・max属性などが追加されるものもあります。
IntegerRangeFieldなど、Postgres専用の型もあります。
詳しくは公式サイトを参照してください。

https://docs.djangoproject.com/ja/2.0/ref/models/fields/
https://docs.djangoproject.com/ja/2.0/ref/contrib/postgres/fields/

選択リストの作り方
フィールドの「choice」オプションを設定することでinput typeがSelectになります。

models.py
class Item(models.Model):

    YEAR_CHOICES = (
        (10, '10代'),
        (20, '20代'),
        (30, '30代'),
        (40, '40代'),
    )

    age = models.IntegerField(verbose_name="年齢", choices=YEAR_CHOICES)

image.png

後述するModelFormクラスでwidgetオプションを設定することにより、ラジオボタンに変更することも可能です。

image.png

1-2.バリデーション

バリデーションは、まずフィールドに合わせた型チェックが行われ、blankオプションの有無にもとづいて必須チェックが適用されます。そのほか以下のようなバリデーションクラスを追加することができます。

バリデーションクラスの例:

検証内容 バリデーションクラス
正規表現によるチェック RegexValidator
最小文字数 MinLengthValidator
最大文字数 MaxLengthValidator
最小値 MinValueValidator
最大値 MaxValueValidator

サンプルコード

models.py
class Item(models.Model):

    name = models.CharField(
        max_length=50,
        verbose_name="名前",
        validators=[validators.RegexValidator(
            regex=u'^[ぁ-んァ-ヶー一-龠]+\u3000[ぁ-んァ-ヶー一-龠]+$',
            message='氏名は漢字・ひらがな・カタカナのみとし、氏と名の間に全角スペースを入れてください',
        )]
    )

    age = models.IntegerField(
        verbose_name="年齢",
        validators=[validators.MinValueValidator(1)])

    check_date = models.DateField(
        verbose_name="確認日",
        validators=[validators.MaxValueValidator(
            date.today(),
            message='本日以前の日付を入力してください',
        )],    
    )

結果

image.png

2.ModelFormを定義する

以下のようにViewを記述することでModelクラスだけでClass Based Viewによる入力フォームが利用できます。

views.py
from django.views.generic.edit import CreateView

class ItemCreateView(CreateView):
    model = Item
    fields = '__all__' #実際は必要なフィールドのみを選択する

しかし、Modelの定義のみでは以下のような細かい要件に対応できません。

・Selectリストではなくradioボタンを使う。
・入力画面と変更画面で編集可項目を変える。
・placeholderやclassなどの属性を追加する

そのような場合はModelFormクラスを別途定義します。
以下の例ではプレースホルダの追加と、選択リストからラジオボタンへの変更をしています。

サンプルコード

forms.py
class ItemForm(forms.ModelForm):


    class Meta:
        model = Item
        fields = ("__all__")
        widgets = {
                    "name": forms.TextInput(attrs={'placeholder':'紀伊 太郎'}),
                    "age": forms.RadioSelect(),

                  }
views.py
class ItemCreateView(CreateView):
    model = Item
    form_class = ItemForm    

結果
image.png

フォームのフィールドも多くの種類が用意されています。公式のページを参考にしてください。
https://docs.djangoproject.com/ja/2.0/ref/forms/fields/

3.templeteに埋め込む

あとはテンプレートにフォームタグを埋めこむだけです。
Django-Crispy-Formsを使うとcontrol-group まわりのタグが自動的に付与されるので、Bootstrapでの利用が簡単になります。
Django-Crispy-Forms 設定等は以下のエントリが参考になります。

Django Formの落穂拾い

サンプル

item_form.html
<!--  -->
{% extends "./base.html" %}
<!--  -->
{% load crispy_forms_tags %}
<!--  -->
{% block content %}
{{ form.certifications.errors }}
<div class="container">
    <div class="row">
        <div class="col-12">
            <h1>入力画面</h1>
        </div>
    </div>
    <div class="row">
        <div class="col-12">
            <div class="float-right">
                <a class="btn btn-outline-secondary" href="{% url 'index' %}">戻る</a>
                <button type="submit" class="btn btn-outline-secondary" form="myform">保存</button>
            </div>
        </div>
    </div>
    <!--  -->
    <div class="row">
        <div class="col-12">
            <form method="post" id="myform">
                {%crispy form%}
            </form>
        </div>
    </div>
    <div class="row">
        <div class="col-12">
            <div class="float-right">
                <a class="btn btn-outline-secondary" href="{% url 'index' %}">戻る</a>
                <button type="submit" class="btn btn-outline-secondary" form="myform">保存</button>
            </div>
        </div>
    </div>
</div>
<!--  -->
{% endblock %}

Crispy-Formsの利用には、別途事前にBootstrapを読み込む必要があります。

base.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <!-- Required meta tags always come first -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <title>サンプルアプリ</title>
    <!-- Bootstrap CSS -->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
        crossorigin="anonymous">
</head>

<body>
    <!--  -->
    {% block content %}
    <!--  -->
    {% endblock %}

    <!-- jQuery first, then Tether, then Bootstrap JS. -->

    <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
        crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
</body>

</html>
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした