22
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Python + Django で、1対多モデルの登録画面を作成する

Last updated at Posted at 2018-07-16

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を使ってインストールしましょう。

cmd.prompt
(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

cmd.prompt
(venv) C:\data\python\myproject>python manage.py startapp extraviews_test

####(2)model
1対多モデルを作成します。Parentモデルに対して、子を3種類もつモデルを作成します。

extraviews_test\models.py
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を編集
アプリケーションをプロジェクトに追加します。

myproject\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
モデルをプロジェクトに追加します。

cmd.prompt
(venv) C:\data\python\myproject>python manage.py makemigrations extraviews_test

データベースにモデルを登録します。

cmd.prompt
(venv) C:\data\python\myproject>python manage.py migrate extraviews_test

####(5)extraviews_test\urls.py
アプリケーションレベルのurls.pyを追加します。startappでは作成されないのでファイルを作成する必要があります。
今回は、新規作成、更新のみとします。「success」は、新規、更新後に表示するだけのページです。

extraviews_test\urls.py
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では作成されていませんのでファイルを作成してください。

extraviews_test\forms.py
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

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_up:Point
formsetは、以下のようにすることが簡単です、
{% for formset in inlines %}

{% endfor %} ですが、formsetを上記のようにform→fieldに展開する場合は、「management_form」が必要になります。 {{ formset.management_form }}

####(9)extraviews_test\templates\extraviews_test\success.html

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)プロジェクトを実行

cmd.prompt
(venv) C:\data\python\myproject>manage.py runserver

(2)URLにアクセス

新規登録
http://localhost:8000/extraviews_test/create/
image.png

編集
http://localhost:8000/extraviews_test/update/1/
image.png

子のモデルには、削除ボタンも表示されています。1対多モデルも簡単に実装できそうです。

22
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?