見出し画像

【タレントダッシュボード構築 #4】社員一覧画面をつくる①

#4からは、#0でご紹介したタレントダッシュボードを実際に作っていく作業をご紹介します。

まずは、社員を一覧表示する以下のような画面を構築していきます。

顔写真付きで社員番号、氏名、年齢、所属、職制、級・号(その「級」をどのくらいの期間務めているか、という意味合いと考えてください)を一覧表示している画面です。汎用的な事例としてお考えいただければと思います。

データモデルの定義

以下の通りモデルを定義します。

[models.py]

from django.db import models
from users.models import User

class Item(models.Model):
    """
    クラス(社員基礎情報)
    """
    seq = models.IntegerField(verbose_name='SEQ',blank=True,null=True,)
    code_7 = models.CharField(verbose_name='社員コード',max_length=7,primary_key=True,default="9999999",)
    name = models.CharField(verbose_name='氏名',max_length=30,blank=True,null=True,)
    name_kana = models.CharField(verbose_name='カナ氏名',max_length=30,blank=True,null=True,)
    honbu = models.CharField(verbose_name='本部',max_length=30,blank=True,null=True,)
    dept = models.CharField(verbose_name='部室',max_length=30,blank=True,null=True,)
    ka = models.CharField(verbose_name='課',max_length=30,blank=True,null=True,)
    shokusei = models.CharField(verbose_name='職制',max_length=30,blank=True,null=True,)
    line = models.IntegerField(verbose_name='ライン',blank=True,null=True,)
    yaku = models.CharField(verbose_name='役職',max_length=30,blank=True,null=True,)
    kyu = models.CharField(verbose_name='級',max_length=5,blank=True,null=True,)
    go = models.IntegerField(verbose_name='号',blank=True,null=True,)
    ent_date = models.DateField(verbose_name='入社日',blank=True,null=True,)
    birth_date = models.DateField(verbose_name='生年月日',blank=True,null=True,)
    choketsu = models.CharField(verbose_name='長欠',max_length=1,blank=True,null=True,)
    kyushoku = models.CharField(verbose_name='休職',max_length=1,blank=True,null=True,)
    shokukei = models.CharField(verbose_name='職系',max_length=1,blank=True,null=True,)
    res_flg = models.CharField(verbose_name='退職フラグ',max_length=1,blank=True,null=True,)
    gakureki = models.ForeignKey(Gakureki,on_delete=models.SET_NULL,null=True,blank=True,)
    # 他のForeignKey項目は割愛
    
    def __str__(self): #リストボックスや管理画面での表示
        return self.code_7
    
    class Meta: #管理画面でのタイトル表示
        verbose_name = '社員基礎情報'
        verbose_name_plural = '社員基礎情報'

ビューの定義

以下の通りビューを定義し、社員一覧画面に「社員基礎情報」のオブジェクト全てを「seq」の昇順で渡すよう定義します。

[views.py]

"""(略)"""

class ItemFilterView(PermissionRequiredMixin, FilterView):

    def get(self, request, **kwargs):
        if request.GET:
            request.session['query'] = request.GET
        else:
            request.GET = request.GET.copy()
            if 'query' in request.session.keys():
                for key in request.session['query'].keys():
                    request.GET[key] = request.session['query'][key]
        return super().get(request, **kwargs)

    def get_queryset(self):
        return Item.objects.all().order_by('seq') #「社員基礎情報」内「seq」の昇順で応答

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data(object_list=object_list, **kwargs)
        return context

表示用htmlファイルの定義

templates/app配下に、ページを表示するためのhtmlファイルを配置します。 まずはこちらのページを参考に_base.htmlおよび_pagination.htmlを用意したうえで、以下の通りitem_filter.htmlを定義します。

[templates/app/item_filter.html]

{% extends "./_base.html" %}
{% block content %}
    {% load crispy_forms_tags %}
    {% load filter %}
    <div class="container">
        <!-- 検索機能については別の投稿にてまとめます -->
        <div id="myModal" class="modal fade" tabindex="-1" role="dialog">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title">検索条件</h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="閉じる">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <form id="filter" method="get">
                        <div class="modal-body">
                            {{ filter.form|crispy }}
                        </div>
                    </form>
                    <div class="modal-footer">
                        <a class="btn btn-outline-dark" data-dismiss="modal">戻る</a>
                        <button type="submit" class="btn btn-outline-dark" form="filter">検索</button>
                    </div>
                </div>
            </div>
        </div>
        
        <div class="row">
            <div class="col-11">
                <!-- ユーザから改善要望を回収するための「イシュー機能」については別の投稿にてまとめます -->
                <a class="btn btn-warning" href="/admin/app/issue/">イシュー登録</a>
                <a class="btn btn-secondary filtered" style="visibility:hidden" href="/?page=1">検索を解除</a>
            </div>
            <div class="col-1">
                <a class="btn btn-outline-dark" data-toggle="modal" data-target="#myModal" href="#">検索</a>
            </div>
        </div>
        <div class="row mt-3">
            <div class="col-10 mx-auto">
                {% include "./_pagination.html" %}
            </div>
        </div>
        <div class="row">
            <div class="col-12">
                    <div class="container">
                      <table class="table table-hover">
                        <thead class="thead-dark">
                          <tr>
                            <th></th>
                            <th>code</th>
                            <th>氏名</th>
                            <th>年齢</th>
                            <th>所属</th>
                            <th>職制</th>
                            <th>級</th>
                            <th>号</th>
                            <th></th>
                          </tr>
                        </thead>
                        <tbody>
                            {% for item in item_list %}
                            <tr>
                            <td><img src={{ item.code_7|photo_new_old_notfound_100 }}></td>
                            <td>{{ item.code_7 }}</td>
                            <td>{{ item.name }}</td>
                            <td>{{ item.birth_date|DateDifY }}</td>
                            <td>{{ item.dept }} {{ item.ka }}</td>
                            <td>{{ item.shokusei }}</td>
                            <td>{{ item.kyu }}</td>
                            <td>{{ item.go }}</td>
                            <td align=right><a class="btn btn-outline-dark " href="{% url 'detail' item.pk %}">詳細</a>
                            </tr>
                            {% endfor %}
                        </tbody>
                      </table>
                    </div>
            </div>
        </div>
        <div class="row mt-3">
            <div class="col-10 mx-auto">
                {% include "./_pagination.html" %}
            </div>
        </div>
    </div>
{% endblock %}

いくつか、通常のhtmlドキュメントではみられない表現が出てきています。これは、動的にページを生成するための「テンプレート」と呼ばれる機能を利用しているものです。

テンプレートは、コンテキストともにレンダリングされます。レンダリングでは、変数が見つかると、コンテキスト内に記録された変数の値に置換され、タグが実行されます。それ以外の文字列は、そのまま出力されます。Django テンプレート言語の構文は、4つの構成要素に分類できます。
・変数
・タグ
・フィルタ
・コメント

https://docs.djangoproject.com/ja/3.2/topics/templates/

item_filter.html で社員情報を表形式で出力する箇所に着目し、それぞれ見ていきます。

<table class="table table-hover">
  <thead class="thead-dark">
    <tr>
      <th></th>
      <th>code</th>
      <th>氏名</th>
      <th>年齢</th>
      <th>所属</th>
      <th>職制</th>
      <th>級</th>
      <th>号</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
      {% for item in item_list %}
      <tr>
      <td><img src={{ item.code_7|photo_new_old_notfound_100 }}></td>
      <td>{{ item.code_7 }}</td>
      <td>{{ item.name }}</td>
      <td>{{ item.birth_date|DateDifY }}</td>
      <td>{{ item.dept }} {{ item.ka }}</td>
      <td>{{ item.shokusei }}</td>
      <td>{{ item.kyu }}</td>
      <td>{{ item.go }}</td>
      <td align=right><a class="btn btn-outline-dark " href="{% url 'detail' item.pk %}">詳細</a>
      </tr>
      {% endfor %}
  </tbody>
</table>

タグの利用

順番が前後しますが、表のbody部分を構成するためにループを回しており、ここでテンプレートの構成要素「タグ」を利用しています。
コンテキストを一件ずつ処理し、表を一行ずつ追加していくという仕組みです。

{% for item in item_list %}

もちろんforだけでなく、他にもさまざまなタグを利用することができます。どんなものを使えるのかについてはこちらをご参照ください。

変数の利用

{{ item.code_7 }}

と記載した部分で、テンプレートの構成要素「変数」を利用しています。item_listの要素(レコード)から、必要な要素(データ)を取得し表示しています。

フィルタの利用

{{ item.birth_date|DateDifY }}

と記載した部分で、テンプレートの構成要素「フィルタ」を利用しています。
あらかじめ組み込まれているフィルタについてはこちらのページをご覧いただくとして、上記では DateDifY という、参照日時点の年齢を計算するための自作フィルタを利用しています。

自作フィルタは以下のように定義しています。

[templatetags/filter.py]

from django import template
from django.core.files.storage import default_storage
from datetime import datetime, date, time, timedelta

register = template.Library()

@register.filter()
def DateDifY(date):
    startdate = date
    today = date.today()
    delta = today.year - date.year
    if (today.month, today.day) < (startdate.month, startdate.day):
        delta -= 1
    return delta

URLの定義

各ファイルを編集後、以下の通りurls.pyを設定すれば、表示可能となります。

[urls.py]

from django.urls import path

from .models import Item, Issue
from .views import *
from . import views

urlpatterns = [
    path('', ItemFilterView.as_view(), name='index'),


次回は、検索機能についてまとめたいと思います。

この記事が気に入ったらサポートをしてみませんか?