django-extra-viewsを使って、1対多モデルの登録画面を実装します。
###1.目的
データを入力するケースでは、単純なモデルだけを登録するようなケースはあまりなく、実践では1対多のリレーションをもつモデルを登録するケースが多いので、このようなケースでdjango-extra-viewsが便利です。
###2.環境
カテゴリ | バージョン |
---|---|
OS | windows 10 home 64bit |
python | 3.6.5 |
django-extra-views | 0.11.0 |
psycopg2 | 2.7.4 |
###3.セットアップ
####(1)django-extra-viewsをインストール
仮想環境で、pipを使ってインストールしましょう。
(venv) C:\data\python\myproject>pip install django-extra-views
####(2)extra-viewsをプロジェクトに配置する
django-extra-viewsについては、以下のサイトを参考にしています。
https://github.com/AndrewIngram/django-extra-views
上記サイトを解凍して、「extra_views」フォルダをプロジェクトのフォルダの直下にコピーします
###4.アプリケーション作成
アプリケーションを作成していきます。
(1)startapp
(venv) C:\data\python\myproject>python manage.py startapp extraviews_test
####(2)model
1対多モデルを作成します。Parentモデルに対して、子を3種類もつモデルを作成します。
from django.db import models
class Parent(models.Model):
name = models.CharField(max_length=255)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
class Child1(models.Model):
name = models.CharField(max_length=255)
child1_column = models.CharField(max_length=255)
Parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
def __unicode__(self):
return self.name
class Child2(models.Model):
name = models.CharField(max_length=255)
child2_column = models.CharField(max_length=255)
comments2 = models.CharField(max_length=255,null=True)
Parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
def __unicode__(self):
return self.name
class Child3(models.Model):
name = models.CharField(max_length=255)
child3_column = models.CharField(max_length=255)
comments3 = models.CharField(max_length=255,null=True)
Parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
def __unicode__(self):
return self.name
####(3)settings.pyを編集
アプリケーションをプロジェクトに追加します。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'extraviews_test', #追加
]
####(4)migrate
モデルをプロジェクトに追加します。
(venv) C:\data\python\myproject>python manage.py makemigrations extraviews_test
データベースにモデルを登録します。
(venv) C:\data\python\myproject>python manage.py migrate extraviews_test
####(5)extraviews_test\urls.py
アプリケーションレベルのurls.pyを追加します。startappでは作成されないのでファイルを作成する必要があります。
今回は、新規作成、更新のみとします。「success」は、新規、更新後に表示するだけのページです。
from django.urls import path
from . import views
app_name = 'extraviews_test'
urlpatterns = [
path('create/', views.ParentCreateView.as_view(), name='create'),
path('update/<int:pk>/', views.ParentUpdateView.as_view(), name='update'),
path('success/', views.SuccessView.as_view(), name='success'),
]
####(6)extraviews_test\views.py
viewもいたってsimpleです。
from django.urls import reverse_lazy
from extra_views import InlineFormSetFactory, CreateWithInlinesView, UpdateWithInlinesView
from django.views.generic import TemplateView
from .forms import ParentForm
from .models import Parent, Child1, Child2, Child3
class Child1Inline(InlineFormSetFactory):
model = Child1
fields = '__all__'
class Child2Inline(InlineFormSetFactory):
model = Child2
fields = '__all__'
class Child3Inline(InlineFormSetFactory):
model = Child3
fields = '__all__'
class ParentCreateView(CreateWithInlinesView):
model = Parent
fields = ['name']
context_object_name = 'parent'
inlines = [Child1Inline, Child2Inline, Child3Inline]
template_name = 'extraviews_test/parent.html'
success_url = reverse_lazy('extraviews_test:success')
class ParentUpdateView(UpdateWithInlinesView):
model = Parent
form_class = ParentForm
inlines = [Child1Inline, Child2Inline, Child3Inline]
template_name = 'extraviews_test/parent.html'
success_url = reverse_lazy('extraviews_test:success')
class SuccessView(TemplateView):
template_name = "extraviews_test/success.html"
####(7)extraviews_test\forms.py
forms.pyはstartappでは作成されていませんのでファイルを作成してください。
from django import forms
from .models import Parent
class ParentForm(forms.ModelForm):
class Meta:
model = Parent
fields = ['name']
def save(self, commit=True):
instance = super(ParentForm, self).save(commit=commit)
if commit:
instance.save()
return instance
####(8)extraviews_test\templates\extraviews_test\parent.html
{% extends "commons/base.html" %}
{% load static %}
{% block title %}
{% endblock title %}
{% block links %}
{% endblock links %}
{% block headertitle %}
ParentAndChilds Models(1対多)
{% endblock %}
{% block content %}
<form action="." method="post">
{% csrf_token %}
<p>親テーブル</p>
<table>
{{ form.as_table }}
</table>
{% for formset in inlines %}
<p>子テーブル(child{{forloop.counter}})</p>
<table class="table table-sm ">
{{ formset.management_form }}
{% for form in formset %}
<!-- ヘッダ出力 -->
{% ifequal forloop.counter0 0 %}
{% for field in form %}
<td class="bg-light">
{% if field.field.widget.is_hidden %}
{% else %}
{{ field.label_tag }}
{% endif %}
</td>
{% endfor %}
{% endifequal %}
<tr>
{% for field in form %}
<td>
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endfor %}
<input class="btn btn-primary" type="submit" value="登録" />
{% endblock content %}
{% block script %}
<script>
$(function(){
})
</script>
{% endblock %}
登録用のページは、サイトのサンプルを少しカスタマイズしています。
Point
formsetは、以下のようにすることが簡単です、
{% for formset in inlines %}
####(9)extraviews_test\templates\extraviews_test\success.html
{% extends "commons/base.html" %}
{% load static %}
{% block headertitle %}
ParentAndChilds Models(1対多)
{% endblock %}
{% block content %}
<h1>Success</h1>
{% endblock content %}
###5.動作確認
(1)プロジェクトを実行
(venv) C:\data\python\myproject>manage.py runserver
(2)URLにアクセス
新規登録
http://localhost:8000/extraviews_test/create/
編集
http://localhost:8000/extraviews_test/update/1/
子のモデルには、削除ボタンも表示されています。1対多モデルも簡単に実装できそうです。