Python Django お問い合わせ管理サイトを作る その5(CRUDの実装)

Django

前回、データベースへデータを追加するところまで実装しました。
今回は、データの詳細表示機能を実装していきます。

さっそく、作り込んでいきたいところですが、前回、実装したデータは、Djangoの提供する管理サイトで確認しました。毎回、管理サイトにアクセスしなくてはいけないのは手間なので、まずは、この一覧ページを自作してデータの登録が終わったら一覧ページに遷移するようにします。

受付一覧ページを作成する

まずは、テンプレートとなるhtmlファイルを作成します。

inquiry_list.html
{% extends 'DjangoApp/base.html' %}


{% block content %}
<div class="row">
    <div class="col col-md-2">
        {% include 'DjangoApp/inquiry_menu.html' %}
    </div>
    <div class="col col-md-10">
        <div class="row my-2 border-bottom bor-der-primary">
            <h2 class="pl-2">受付一覧</h2>
        </div>
        <table>
            <tr>
                <th></th>
                <th>緊急度</th>
                <th>ステータス</th>
                <th>件名</th>
                <th>顧客名</th>
                <th>受付日時</th>
            </tr>
            {% for item in inquirys %}
            <tr>
                <td></td>
                <td>{{ item.emergency }}</td>
                <td>{{ item.status }}</td>
                <td>{{ item.subject }}</td>
                <td>{{ item.customer }}</td>
                <td>{{ item.reception_datetime }}</td>
            </tr>
            {% endfor %}
        </table>
    </div>
</div>

{% endblock %}

入力データを表形式で表示するようにしました。
{% for … %}{% endfor %}によって、データを一つずつ取り出して、設定しています。
inquirysは、view関数から渡します。

次に、views.pyにリスト用の関数を定義します。

views.inquiry-list.py
from .models import Inquiry

def inquiry_list(request):
    inquirys = Inquiry.objects.all().order_by('-reception_datetime')
    
    context = {
        'inquirys': inquirys, 
    }

    return render(request, 'DjangoApp/inquiry_list.html', context)

ソースがだんだんと長くなったので、変更があった部分のみ記載しています。

モデルデータを読み込むためにInquryクラスをインポートしています。
リスト用関数の冒頭の一行がデータを読み込んでいるところです。
モデル名.objects.all()でデータベースに保存済みのデータ全てを取り出すことができます。
all()の部分をfilter()メソッドに置き換えてあげると、条件指定検索を行うことができます。

取り出したデータに対して、さらに、order_byメソッドを追加しました。
order_byメソッドによって、データの並び替えを行います。与える引数は、どのデータを基準に並べ替えるかを決めます。
引数の先頭に「-」をつけることで、降順に並び替えることができます。「-」がなければ、昇順になります。

あとは、先ほど作成したhtmlファイルに、読み込んだデータを引き渡して表示させているだけです。

最後に、urls.pyにリストページへのURL情報を追記しておしまいです。

views.urls.py
from django.urls import path

from .views import dashboard, inquiry_create, inquiry_list


app_name = 'DjangoApp'
urlpatterns = [
    path('', dashboard, name='dashboard'),
    path('create/', inquiry_create, name='inquiry-create'),
    path('list/', inquiry_list, name='inquiry-list'), 

]

では、アクセスしてみましょう。

データが表示されましたね。
ちょっと、見た目が残念すぎるので少し手を加えます。

<table>タグを<table class=”table table-dm”>で置き換えます。
ページを更新すると、

これだけで、だいぶ違いますね。Bootstrapすごいです。

もう一つ手を加えなくてはいけません。
緊急度とステータスが「0」という値で表示されています。
バグっているわけではなく、データベースにはこの値で登録されているということです。
これでは、人が見たときに何を表しているか不明です。
データ入力時と同じように、「緊急」や「通常」といった感じでわかる表示に変えたいところです。

対象の部分を以下のように書き換えます。

<td>{{ item.emergency }}</td> → <td>{{ item.get_emergency_display }}</td>
<td>{{ item.status }}</td> → <td>{{ item.get_status_display }}</td>

フィールド名を「get_」と「_display」で挟んであげるだけです。表示が以下のように変わります。

それでは、データの入力が終わったときにこのページ飛ぶように変更します。
view関数のinquiry_createのredirect部分を以下のように書き換えるだけですね。

views.py
def inquiry_create(request):
    initial_data = {
        'reception_datetime': datetime.now(), 
    }

    form = InquiryModelForm(initial=initial_data)

    if request.method == 'POST':
        form = InquiryModelForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('DjangoApp:inquiry-list')

    context = {
        'form': form, 
    }
    
    return render(request, 'DjangoApp/inquiry_form.html', context)

実際にデータを追加してみましょう。

データを追加すると、一覧ページに飛ぶことが確認できますね。
ちなみに、先程データを降順で並び替えるようにしたことも反映されていることがわかります。

次に、同じ表示系の詳細表示部分を作っていきたいと思います。CRUDのR部分ですね

Read機能を作成する

リストの時同様、テンプレートから用意していきます。
今回は、最初からBootstrapのクラスを指定して見た目をそれっぽくしています。

inquiry_detail.html
{% extends 'DjangoApp/base.html' %}
{% load verbose_name %}

{% block content %}
<div class="row">
    <div class="col col-md-2">
        {% include 'DjangoApp/inquiry_menu.html' %}
    </div>
    <div class="col col-md-10">
        <div class="row my-2 border-bottom bor-der-primary">
            <h2 class="pl-2">受付入力</h2>
        </div>
        <div class="row pl-2">
            <div class="row col-md-12">
                <p class="col col-md-2">{% get_verbose_field_name inquiry 'emergency' %}</p>
                <p class="col col-md-10 text-primary">{{ inquiry.get_emergency_display }}</p>
            </div>
            <div class="row col-md-12">
                <p class="col col-md-2">{% get_verbose_field_name inquiry 'status' %}</p>
                <p class="col col-md-10 text-primary">{{ inquiry.get_status_display }}</p>
            </div>
            <div class="row col-md-12">
                <p class="col col-md-2">{% get_verbose_field_name inquiry 'subject' %}</p>
                <p class="col col-md-10 text-primary">{{ inquiry.subject }}</p>
            </div>
            <div class="row col-md-12">
                <p class="col col-md-2">{% get_verbose_field_name inquiry 'customer' %}</p>
                <p class="col col-md-10 text-primary">{{ inquiry.customer }}</p>
            </div>
            <div class="row col-md-12">
                <p class="col col-md-2">{% get_verbose_field_name inquiry 'category' %}</p>
                <p class="col col-md-10 text-primary">{{ inquiry.category }}</p>
            </div>
            <div class="row col-md-12">
                <p class="col col-md-2">{% get_verbose_field_name inquiry 'reception_datetime' %}</p>
                <p class="col col-md-10 text-primary">{{ inquiry.reception_datetime }}</p>
            </div>
            <div class="row col-md-12">
                <p class="col col-md-2">{% get_verbose_field_name inquiry 'content' %}</p>
                <p class="col col-md-10 text-primary">{{ inquiry.content }}</p>
            </div>
            <div class="row col-md-12">
                <p class="col col-md-2">{% get_verbose_field_name inquiry 'request' %}</p>
                <p class="col col-md-10 text-primary">{{ inquiry.request }}</p>
            </div>
        </div>
    </div>
</div>
{% endblock %}

基本的には、リストのときと同じような感じです。
フィールドの名前 登録されている値
が1レコード分表示されるようになっています。

見慣れないのが、フィールドの名前を表す部分の get_verbose_field_name …です。
これは、標準で用意されているものではなく別で定義した独自のテンプレートタグ・フィルタと呼ばれるものです。

テンプレートタグ・フィルタとは、テンプレート(HTML側)にロジックを組み込むための機能になります。

フィルタは、テンプレートに組み込んだ変数 {{ var }}の表示をカスタマイズしたい場合に利用します。
{{ var|filter }}のように記述します。例えば、 {{ name|lower }}とすれば、name変数の値をすべて小文字へ変換します。

タグは、テンプレートにロジックを組み込むために利用します。
{% tag %}のように記述します。すでに、{% for …%}{% endfor %}などで利用していますね。

Djangoでは、自分で作成したタグやフィルタを利用することができます。

タグを利用するためには、まず、アプリケーションフォルダの中にtemplatetagsフォルダを作成します。
そして、その中に処理を記述するpyファイルを作成します。
今回の場合、以下のようになります。

.
├── DjangoApp
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-36.pyc
│   │   ├── admin.cpython-36.pyc
│   │   ├── forms.cpython-36.pyc
│   │   ├── models.cpython-36.pyc
│   │   ├── urls.cpython-36.pyc
│   │   └── views.cpython-36.pyc
│   ├── admin.py
│   ├── apps.py
│   ├── forms.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── 0002_auto_20200514_2153.py
│   │   ├── __init__.py
│   │   └── __pycache__
│   │       ├── 0001_initial.cpython-36.pyc
│   │       ├── 0002_auto_20200514_2153.cpython-36.pyc
│   │       └── __init__.cpython-36.pyc
│   ├── models.py
│   ├── static
│   │   └── DjangoApp
│   │       ├── css
│   │       │   └── style.css
│   │       ├── img
│   │       │   └── logo.png
│   │       └── js
│   ├── templates
│   │   └── DjangoApp
│   │       ├── base.html
│   │       ├── dashboard.html
│   │       ├── footer.html
│   │       ├── head.html
│   │       ├── header.html
│   │       ├── inquiry_detail.html
│   │       ├── inquiry_form.html
│   │       ├── inquiry_list.html
│   │       ├── inquiry_menu.html
│   │       └── javascripts.html
│   ├── templatetags
│   │   ├── __init__.py
│   │   ├── __pycache__
│   │   │   ├── __init__.cpython-36.pyc
│   │   │   └── verbose_name.cpython-36.pyc
│   │   └── verbose_name.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── DjangoProject
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-36.pyc
│   │   ├── settings.cpython-36.pyc
│   │   ├── urls.cpython-36.pyc
│   │   └── wsgi.cpython-36.pyc
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── db.sqlite3
└── manage.py

加えて、作成したtemplatetagsフォルダの下に__init__.pyファイルを作成してください。
ファイルの中身は空のままでOKです。(フォルダが、pythonモジュールではないよということを教えるためのファイルです)

今回作成したverbose_name.pyファイルの中身は以下のようになります。

verbose_name.py
from django import template


register = template.Library()

@register.simple_tag
def get_verbose_field_name(instance, field):
    return instance._meta.get_field(field).verbose_name

タグとして定義する場合は、関数の先頭に@register.simple_tagをつけます。
今回欲しい処理は、モデルのフィールド名を取り出す処理です。モデルを作成したときに指定したverbose_nameを
取り出しています。

作成したカスタマイズタグを利用する場合、テンプレートファイルの先頭部分に
{% load カスタマイズタグファイル名 %}を記述しておきます。
もう一つ、settings.pyファイルにもファイルの存在を記述します。

settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'libraries': {
                'verbose_name': 'DjangoApp.templatetags.verbose_name', 
            }, 
        },
    },
]

TEMPLATES内の’OPTIONS’に’libraries’を追加します。

次に詳細表示処理を呼び出すview関数を定義します。

views.py
def inquiry_detail(request, pk):
    inquiry = Inquiry.objects.get(id=pk)
    
    context = {
        'inquiry': inquiry, 
    }
    
    return render(request, 'DjangoApp/inquiry_detail.html', context)

Inquiryモデルから、表示したいレコードのidを指定してデータを読み出しています。
idを指定するために関数の引数には、pk引数を追加しました。(pk = primary key)

最後に、URL情報を追加します。

DjangoApp/urls.py
from django.urls import path

from .views import dashboard, inquiry_create, inquiry_list, inquiry_detail


app_name = 'DjangoApp'
urlpatterns = [
    path('', dashboard, name='dashboard'),
    path('create/', inquiry_create, name='inquiry-create'),
    path('list/', inquiry_list, name='inquiry-list'),
    path('<int:pk>/', inquiry_detail, name='inquiry-detail'), 

]

<int:pk>によって、レコードを指定します。レコードを指定する値pkは、整数値ですよという意味です。
アクセスするときのURLは、http://127.0.0.1:8000/DjangoApp/1/のような表記になります。
ここで与えられたpkの値は、先程のview関数のpk引数の値になります。そのため、両者の変数名は同じである必要があります。

では、早速アクセスしてみましょう。

できました。
今回は、この辺にしておきます。
次回は、登録済みデータの更新・削除機能を実装します。

コメント

タイトルとURLをコピーしました