ホーム >  Python > Django >  (ModelChoiceField)登録画面で別モデルを参照したラジオボタンを表示(Django)

投稿日:   |  最終更新日:

(ModelChoiceField)登録画面で別モデルを参照したラジオボタンを表示(Django)

DjangoPython

Djangoで登録処理をするとき、別に用意したモデル(マスタテーブル)を参照してラジオボタン(選択ボタン)を表示します。

ラジオボタン一覧の表示

Djangoで作ったページにラジオボタン一覧を表示します。ラジオボタンで使うデータは、例えば以下のようにmodels.pyに直接データを記述して一覧データを作る方法があります。

from django import forms

ANIMAL_CHOICES = [
    (1, 'ねこ'),
    (2, 'いぬ'),
    (3, 'うさぎ'),
]

class anim_type(models.Model):

    type_name = models.TextField(verbose_name="種類名", choices=ANIMAL_CHOICES, blank=True)

    def __str__(self):
        return str(self.type_name)

「ANIMAL_CHOICES」という配列にデータを記述して、「type_name」の「choices」で指定すれば、リストデータとして利用することができます。あとはテンプレートでドロップダウンリストやラジオボタンリストとして表示できます。

直接データを記述するのもいいですが、予めデータベース(mysql等)にデータを登録し、あとは呼び出すだけでリストデータを表示したい場合があります。

仕様

①トップページです。「ドメイン/」にアクセスします。画面右上にログインページへのリンクがあります。

②ラジオボタン一覧を表示するペットの飼主情報を表示するページです。「ドメイン/animals_create」にアクセスします。

登録プログラムは、汎用ビューのCreateViewを使います。

④登録ボタンを押すと、登録が完了してトップページに戻ります。

準備

当サイトでは、Vagrantでゲスト環境(仮想環境)を作ってDjangoを動かいていますが、それ以外の環境でも操作方法はだいたい同じです。

①Djangoプロジェクトを生成します。

VagrantのDjangoで作るWebアプリケーション(その1 プロジェクト生成)

②Djangoアプリケーションを作ります。

VagrantのDjangoで作るWebアプリケーション(その2 プロジェクトの初期設定)

環境

OS CentOS 7.1.1503
pyenv 1.1.3-5-g7dae197
Anaconda 3-4.3.0
MariaDB 5.5.52-1.el7
Apache 2.4.6
mod_wsgi 4.6.3
Django 2.0.3


ラジオボタン一覧機能概要

①以下のようなDjangoアプリ構成を作ります。プロジェクト名が「pj1」で、アプリケーション名が「users」です。プロジェクトの設定は済ませたものとします。赤字のファイルは、今回変更する部分です。

  • pj1/
  • media/
  • pj1/
  • __init__.py
  • settings.py
  • urls.py
  • wsgi.py
  • users/
  • __init__.py
  • admin.py
  • apps.py
  • urls.py
  • views.py
  • models.py
  • templates/
  • index.html
  • animals_createform.html

モデル/models.py

usersディレクトリ下のmodels.pyを変更します。

①マスターテーブルにあたる「pets」モデルと、スレーブテーブルにあたる「anim_type」モデルを作成します。

from django.db import models

class anim_type(models.Model):

    animal_type = models.IntegerField(verbose_name="動物種類", default=0)
    type_name = models.TextField(verbose_name="種類名", blank=True)

    def __str__(self):
        return str(self.type_name)


class pets(models.Model):

    anim_name = models.TextField(verbose_name="名前", blank=True)
    owner_name = models.TextField(verbose_name="飼主名前", blank=True)
    animal_type = models.ForeignKey(anim_type, on_delete=models.SET_NULL, null=True)


    def __str__(self):
        return self.owner_name

②models.pyを記述したら、マイグレーションファイルを作成します。

manage.py makemigrations

③マイグレーションファイルをデータベースに適用します。

manage.py migrate

解説

class anim_type(models.Model):

    animal_type = models.IntegerField(verbose_name="動物種類", default=0)
    type_name = models.TextField(verbose_name="種類名", blank=True)

    def __str__(self):
        return str(self.type_name)

「anim_type」モデルに”いぬ”や”ねこ”のように具体的な動物名を格納します。これら動物名をラジオボタンとして出力します。

class pets(models.Model):

    anim_name = models.TextField(verbose_name="名前", blank=True)
    owner_name = models.TextField(verbose_name="飼主名前", blank=True)
    animal_type = models.ForeignKey(anim_type, on_delete=models.SET_NULL, null=True)


    def __str__(self):
        return self.owner_name

    def get_filename(self):
        return os.path.basename(self.animal_image_jpeg.name)

ペットの名前を登録するためのモデルです。ラジオボタン一覧のペット名が選択されると、このモデルに「anim_type」モデルのIDが登録されます。


モデルへデータ登録

「anim_type」モデルへ動物の種類名を登録します。

①以下のコマンドでシェルを立ち上げます。

python manage.py shell

②とりあえず、「ねこ」「いぬ」「うさぎ」を登録してみます。

>>> from users.models import anim_type
>>> anim_1 = anim_type(animal_type = 1, type_name = 'ねこ')
>>> anim_1.save()
>>> anim_2 = anim_type(animal_type = 2, type_name = 'いぬ')
>>> anim_2.save()
>>> anim_3 = anim_type(animal_type = 3, type_name = 'うさぎ')
>>> anim_3.save()

③登録できたか確認します。

>>> anim_type = anim_type.objects.all()
>>> anim_type
<QuerySet [<anim_type: ねこ>, <anim_type: いぬ>, <anim_type: うさぎ>]>

④シェルコマンドを終了します。

>>> exit()

アプリケーション/urls.py

①urls.pyに、以下のurlを設定します。

from django.conf.urls import url                                                   
from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
    url("^animals_create/$",views.AnimCreateView.as_view(),name='animals_create'),
]

フォームの定義

フォームを定義します。

①アプリケーションのディレクトリにforms.pyを作成し定義します。

from django import forms
from .models import (
    pets, anim_type, 
)

class AnimForm(forms.ModelForm):

    animal_type = forms.ModelChoiceField(
        label="種類",
        queryset=anim_type.objects.all(),
        widget=forms.RadioSelect,
        empty_label=None,)

    class Meta:
        model = pets

        fields = ('anim_name', 'owner_name', 'animal_type')
        widgets = {
            'anim_name': forms.TextInput(),
            'owner_name': forms.TextInput(),
        }

 
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['anim_name'].widget.attrs['class'] = 'form-control'
        self.fields['anim_name'].widget.attrs['placeholder'] = 'ペットの名前'
        self.fields['anim_name'].widget.attrs['maxlength'] = '20'
        self.fields['owner_name'].widget.attrs['class'] = 'form-control'
        self.fields['owner_name'].widget.attrs['placeholder'] = '飼主名前'
        self.fields['owner_name'].widget.attrs['maxlength'] = '20'

解説

    animal_type = forms.ModelChoiceField(
        label="種類",
        queryset=anim_type.objects.all(),
        widget=forms.RadioSelect,
        empty_label=None,)

ModelChoiceField(** kwargs)

単一のモデルオブジェクトの選択を許可します。ただし、アイテム数を増やしすぎないようにしましょう。(外部キーを通すことに適しています。)

引数 説明
label このフィールドのラベルを指定します。
widget フォームのタイプを指定します。ラジオボタンの場合は「forms.RadioSelect」です。
queryset QuerySetフィールドの選択肢の派生元です。データ元であるモデルを指定します。
empty_label ドロップダウンリストの空の”選択肢”の表示方法を指定します。デフォルトは「”———“」です。

エラーメッセージキー:required、invalid_choice

アプリケーション/views.py

アプリケーション(users)のビューに処理を書きます。

from django.shortcuts import render, redirect
from .forms import AnimForm
from .models import pets

def index(request):
  context = {
    'users':request.user,
  }
  #return render(request, 'index.html', context)

class AnimCreateView(generic.CreateView):

    model = pets
    form_class = AnimForm
    template_name = 'tbl_animals_createform.html'

    success_url = ('/')

解説

ジェネリックビュー:generic.CreateViewを利用します。

class AnimCreateView(generic.CreateView):
 
    model = pets
    form_class = AnimForm
    template_name = 'animals_createform.html'
 
    success_url = ('/')
  • モデルに「pets」を指定します。
  • フォームクラス(htmlに表示するフォーム)に、「AnimForm」を指定します。
  • テンプレートに「animals_createform.html」を指定します。
  • 登録後、index(トップページ)にリダイレクトします。


テンプレート/animals_createform.html

htmlのテンプレートファイルを作成します。

{% extends 'base.html' %}
{% block content %}
{% load static%}

<div class="container-fluid" style="margin-top: 60px;">
  <div class="container">
    <form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <div class="row">
      <div class="col-lg-12 mb-3">
        <div class="card">
          <div class="card-header bg-secondary" role="tab" id="headingTwo">
            <h5 class="mb-0">動物の名前</h5>
          </div><!-- /.card-header -->
          <div class="card-body  p-4">
            <div class="form-group row">
            <label for="{{ form.anim_name.id_for_label }}" class="col-sm-3 col-form-label">{{ form.anim_name.label_tag }}</label>
              <div class="col-sm-9">{{ form.anim_name }}</div>
            </div>
            <div class="form-group row">
            <label for="{{ form.owner_name.id_for_label }}" class="col-sm-3 col-form-label">{{ form.owner_name.label_tag }}</label>
              <div class="col-sm-9">{{ form.owner_name }}</div>
            </div>
          </div><!-- end card-body-->
        </div><!-- end card-->
      </div><!-- end col-lg-12-->

      <div class="col-lg-12 mb-3">
        <div class="card">
          <div class="card-header bg-secondary" role="tab" id="headingTwo">
            <h5 class="mb-0">動物の種類の選択</h5>
          </div><!-- /.card-header -->
          <div class="card-body">
            <ul style="list-style: none;" class="py-3">
            {% for value, text in form.animal_type.field.choices %}
            <li>
            <input name="animal_type" type="radio" value="{{ value }}" {% if value == form.animal_type.value %}checked{% endif %}>{{ text }}
            </li>
            {% endfor %}
            </ul>
          </div><!-- end card-body-->
        </div><!-- end card-->
      </div><!-- end col-lg-12-->

      <div class="col-lg-12 p-3">
        <button class="btn btn-primary col-lg-12" type="submit">登録</button>
      </div><!-- end col-lg-12-->
    </div><!-- end row-->
    </form>
  </div><!-- end container-->
</div><!-- end container-fluid-->
{% endblock %}

解説

            <label for="{{ form.anim_name.id_for_label }}" class="col-sm-3 col-form-label">{{ form.anim_name.label_tag }}</label>
              <div class="col-sm-9">{{ form.anim_name }}</div>

モデル「pets」の入力フォームを表示します。上記の例は、anim_nameフィールドです。「form.anim_name.label_tag 」で、フォームのラベルを表示します。「form.anim_name」で、入力フォームを表示します。

            <ul style="list-style: none;" class="py-3">
            {% for value, text in form.animal_type.field.choices %}
            <li>
            <input name="animal_type" type="radio" value="{{ value }}" {% if value == form.animal_type.value %}checked{% endif %}>{{ text }}
            </li>
            {% endfor %}
            </ul>

ラジオボタンを表示します。「form.animal_type.field.choices」をvalue変数・text変数に代入してデータの最初から最後までループします。

base.htmlは、以下の通りです。

{% load static %}
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="shortcut icon" href="{% static 'main/img/favicon.ico' %}">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <title>ユーザ登録</title>
 
    <!-- Bootstrap -->
    <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
    <link href="{% static 'css/bootstrap-reboot.min.css' %}" rel="stylesheet">
    <link href="{% static 'css/bootstrap-grid.min.css' %}" rel="stylesheet">
    <script src="{% static 'js/jquery-3.2.1.min.js' %}"></script>
    <script src="{% static 'js/bootstrap.min.js' %}"></script>
    <style>
      .my-form {
        width: 640px;
        margin: 0 auto;
      }
 
      @media screen and (max-width: 768px) {
        .my-form {
          width: 100%;
        }
      }
 
      .errorlist li {
        list-style-type: none;
      }
 
      .errorlist {
        color: red;
        margin-left: 0;
        padding-left: 0;
      }
    </style>
  </head>
 
  <body>
    <nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse">
      <button class="navbar-toggler navbar-toggler-right"
      type="button" data-toggle="collapse" data-target="#navbarNav"
      aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
      </button>
      <a class="navbar-brand" href="#">App1</a>
      <div class="collapse navbar-collapse" id="navbarNav">
        <ul class="navbar-nav">
          <li class="nav-item active">
            <a class="nav-link" href="{% url 'users:index' %}">Home</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="{% url 'users:profile' %}"">プロフィール</a>
          </li>
          {% if user.is_authenticated %}
          <li class="nav-item">
          <a class="nav-link" href="{% url 'users:post' %}">投稿</a>
          </li>
          {% endif %}
        </ul>
        <ul class="nav navbar-nav ml-auto">
         <li class="nav-item">
          <a class="nav-link" href="{% url 'users:contact' %}">Contact</a>
          </li>
          <li class="nav-item">
          {% if user.is_authenticated %}
          <a class="nav-link" href="{% url 'users:logout' %}">ログアウト</a>
          {% else %}
          <a class="nav-link" href="{% url 'users:login' %}">ログイン</a>
          {% endif %}
          </li>
        </ul>
      </div>
    </nav>
      {% block content %}
      {% endblock %}
    <!-- Bootstrap -->
    <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
    <link href="{% static 'css/bootstrap-reboot.min.css' %}" rel="stylesheet">
    <link href="{% static 'css/bootstrap-grid.min.css' %}" rel="stylesheet">
    <!-- js -->
    <script src="{% static 'js/bootstrap.min.js' %}"></script>
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </body>
</html>


トラックバック用のURL
プロフィール

名前:イワサキ ユウタ 職業:システムエンジニア、ウェブマスター、フロントエンドエンジニア 誕生:1986年生まれ 出身:静岡県 特技:ウッドベース 略歴 20

最近の投稿
人気記事
カテゴリー
広告