投稿日: | 最終更新日:
(ModelChoiceField)登録画面で別モデルを参照したラジオボタンを表示(Django)
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>
- Python 114
- 制作 54
- RaspberryPi 41
- Django 40
- WordPress 40
- Linux 27
- VPS 22
- JavaScript 21
- PHP 20
- HTML・CSS 19
- AWS 16
- 仮想環境 15
- レスポンシブデザイン 13
- マイコン 11
- WEB全般 11
- 動画製作 9
- Webサービス 8
- 統合開発環境 8
- 機械学習 8
- PyCharm 7
- jQuery 7
- AfterEffects 7
- 起業・設立 7
- Django REST framework 6
- C# 6
- デザイン 6
- SEO 6
- pydata 6
- Visual Studio 5
- 数学 5
- 携帯サイト 5
- heroku 5
- Mac 5
- illustrator 5
- node.js 5
- Anaconda 5
- Nginx 4
- Jupyter Notebook 4
- インフラ 4
- Google Colaboratory 4
- symfony 4
- Webスクレイピング 3
- photoshop 3
- Go言語 3
- PC 3
- ツール 3
- Docker 3
- facebook 3
- 作業効率化 3
- データベース 3
- Cloud9 3
- コマンド 2
- micro:bit 2
- Kali Linux 2
- Webサーバー 2
- MariaDB 2
- ドローン 2
- コンテナ 2
- DaVinci Resolve 2
- ネットワーク 2
- Java 2
- movie 2
- PCDJ 2
- 音楽 2
- XSERVER 2
- Ansible 1
- Vue.js 1
- JSON 1
- Bootstrap 1
- バージョン管理システム 1
- SSL 1
- S3 1
- ムームードメイン 1
- ネットワーク 1
- アニメーション 1
- D3.js 1
- Rhino 1
- アニメ 1
- git 1
- windows 1
- アクセス解析 1
- スマートフォン 1
- アフィリエイトノウハウ 1
- 知識 1
- TypeScript 1
- 役立つ本・書籍 1
- データサイエンス 1
- ESP32 1
- AI 1
- ownCloud 1
- API 1