「Python + Django + SlickGrid で一覧表示」で、gridライブラリを使った一覧表示を試してみましたが、Slick Gridより、簡単に実装できる「ag-grid」というライブラリに出会ったので、Djangoと連携する例を投稿しておきます。
1.インストール
ag-gridは、javascript, angular, react, vueなどで使用できると書いてありますが、今回は、javascript版を使用します。
(1)ダウンロード
GitHUBからダウンロードします。必要なcss, jsファイルは、distフォルダの中にあります。
(2)アプリケーションを作成する。
Djangoなので、サンプルアプリケーションを作成します。
- 今回のメインとなるアプリケーションを作成する
python manage.py startapp hellojson2
- db用のアプリケーションを使います。
Python+DjangoでDBの検索処理をモジュール化してみる
python manage.py startapp commons
- model用のアプリケーション
modelはすべてのアプリケーションで使うので、別でアプリケーションを作成しておきます。
python manage.py startapp entities
- アプリケーションの構成
以下のようなファイル構成になります。
staticフォルダの下のcss, jsフォルダに、ダウンロードしたag-gridのcss, jsを配置します。
※ ag-gridのほかに、JQueryも使います。bootstrapは好みに合わせて配置してください。
myproject
│ db.sqlite3
│ manage.py
│
├─commons
・・・
│ │ dbutils.py
・・・
│
├─entities
│ │ admin.py
│ │ models.py
・・・
├─hellojson2
│ │ admin.py
│ │ apps.py
│ │ models.py
│ │ tests.py
│ │ urls.py
│ │ views.py
・・・
├─myproject
│ │ settings.py
│ │ urls.py
│ │ wsgi.py
│ │ __init__.py
・・・
├─static
・・・
│ ├─css
│ │ ag-grid.css
│ │ ag-theme-balham.css
│ │ bootstrap.min.css
│ │ bootstrap.min.css.map
・・・
│ ├─js
│ │ ag-grid-community.min.noStyle.js
│ │ bootstrap.bundle.min.js
│ │ bootstrap.bundle.min.js.map
│ │ jquery-3.3.1.min.js
・・・
2.アプリケーションの概要
- 従業員マスタのメールアドレスを一括更新するアプリケーションです。
- 画面に、従業員マスタの一覧を表示し、ユーザは、更新対象とする行をチェックして更新します。
- 初期表示時は、一覧は表示されません。検索ボタンで表示します。
- 更新ボタンを押すと、従業員マスタのメールアドレスを「bbb@localhost.com」に変更します。
3.実装
(1)model
- modelの作成
from django.db import models
# Register your models here
class Department(models.Model):
"""
所属マスタ
"""
deptid = models.CharField(max_length=16)
deptname = models.CharField(max_length=64)
upperdeptid = models.CharField(max_length=16)
def __str__(self):
return self.deptname
class Employee(models.Model):
"""
従業員マスタ
"""
empid = models.CharField(max_length=16)
empname = models.CharField(max_length=64)
deptid = models.CharField(max_length=16)
mailaddress = models.CharField(max_length=128)
def __str__(self):
return self.empname
- プロジェクトにアプリケーションを追加する
INSTALLED_APPS = [
・・・
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bootstrap4', #追加
'entities', #追加
'hellojson2', #追加
]
- migrate
python manage.py makemigrations entities
python manage.py migrate entities
(2)HTML
htmlのコメントにポイントを入れています。
{% extends 'commons/base.html' %}
{% load static %}
{% block links %}
<link rel="stylesheet" href="{% static 'css/ag-grid.css' %}" type="text/css"/>
<link rel="stylesheet" href="{% static 'css/ag-theme-balham.css' %}" type="text/css"/>
<style media="screen">
.form-control{border: none !important;}
</style>
<script src="{% static 'js/ag-grid-community.min.noStyle.js' %}"></script>
{% endblock %}
{% block headertitle %}
一括更新 サンプル
{% endblock %}
{% block content %}
<br/>
<div class="form-control">
<p>選択した行のメールアドレスを一括更新します。</p>
</div>
<div class="form-control text-right">
<button class="btn btn-sm btn-primary" onclick="search(); return false;">検索</button>
<button class="btn btn-sm btn-primary" onclick="sendData(); return false;">更新</button>
</div>
<div id="myGrid" style="height: 600px; width:100%; margin-top:10px;" class="ag-theme-balham"></div>
<script type="text/javascript">
// ※DjangoとAjax通信するときは、CSRF対策をしておく必要がある。
// django CSRF対策 ここから
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
crossDomain: false, // obviates need for sameOrigin test
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
// django CSRF対策 ここまで
//検索ボタン
function search(){
//fetchを
fetch("{% url 'hellojson2:getlist' %}").then(function(response) {
return response.json();
}).then(function(data) {
gridOptions.api.setRowData(data);
})
}
//更新ボタン
function sendData() {
//ag-grid:選択した行の一覧を取得する。
var selectedNodes = gridOptions.api.getSelectedNodes()
var selectedData = selectedNodes.map( function(node) { return node.data })
if (selectedData.length == 0) {
alert("選択してください。")
return;
}
//fetchではなく、JQuery Ajaxを使う例。
$.ajax('/hellojson2/senddata/',
{
type: 'post',
data: { query: JSON.stringify(selectedData) },
'dataType': 'json'
}
)
// 更新成功時に{"status": "OK"}が返ってくる
.done(function(data) {
alert(JSON.stringify(data));
})
// 検索失敗時には、その旨をダイアログ表示
.fail(function( jqXHR, textStatus, errorThrown ) {
window.alert('通信エラーが発生しました。 status=' + textStatus);
})
.always(function( jqXHR, textStatus ) {
});
}
// ag-grid: 列定義
// empidにcheckboxSelectionをセットすることで、checkboxを表示
// headerCheckboxSelectionは、ヘッダにcheckboxを表示
// empidは、リンクボタンで表示するようにしています。
var columns = [
{headerName: "従業員コード", field: "empid", headerCheckboxSelection: true, checkboxSelection: true, filter: true, editable: true,
cellRenderer: function(params) {
//将来的には、詳細表示などのURLをセットする。ひとまずgoogleのサイトを開く
return '<a href="https://www.google.com" target="_blank">'+ params.value+'</a>'
},
},
{headerName: "氏名", field: "empname", filter: true},
{headerName: "メールアドレス", field: "mailaddress", filter: true},
{headerName: "所属コード", field: "deptid", filter: true},
{headerName: "所属名", field: "deptname", filter: true}
];
//ag-grid: データ(JSON)
var data = {{data|safe}}
//ag-grid: Options
//複数行選択にしています。
var gridOptions = {
columnDefs: columns,
rowSelection: 'multiple'
// rowData: data
};
// ag-grid: lookup the container we want the Grid to use
var eGridDiv = document.querySelector('#myGrid');
// ag-grid: create the grid passing in the div to use together with the columns & data we want to use
new agGrid.Grid(eGridDiv, gridOptions);
// ag-grid: 初期表示時のデータをセットしておく。
gridOptions.api.setRowData(data);
</script>
{% endblock %}
(3)ルーティング
from django.urls import path
from . import views
app_name = 'hellojson2'
urlpatterns = [
path('', views.index, name='index'), # 初期表示
path('getlist/', views.getlist, name='getlist'), # 検索
path('senddata/', views.senddata, name='senddata'), # 更新
]
(4)view.py
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse
import json
from commons.dbutils import exec_query
from django.views.decorators.csrf import ensure_csrf_cookie
from entities.models import Employee
# list
def index(request):
data = []
jsondata = json.dumps(data, ensure_ascii=False, indent=2)
return render(request,
'hellojson2/index.html',
{'form_name': 'hellojson2', 'data': jsondata})
def getlist(request):
sqltext="""SELECT
a.id
, a.empid
, a.empname
, a.deptid
, a.mailaddress
, b.deptname
FROM
public.entities_employee a
INNER JOIN
public.entities_department b
on a.deptid=b.deptid
ORDER BY
a.id
; """
emplist=exec_query(sqltext);
jsondata = json.dumps(emplist, ensure_ascii=False);
return HttpResponse(jsondata)
@ensure_csrf_cookie
def senddata(request):
txt = request.POST['query']
datas = json.loads(txt)
for element in datas:
# print(element)
entity = Employee.objects.filter(id=element['id']).first()
entity.mailaddress = 'bbb@localhost.com'
entity.save()
data = {'status': 'OK'}
json_str = json.dumps(data, ensure_ascii=False, indent=2)
return HttpResponse(json_str)
(5)プロジェクトのルーティング
最後に、myproject\urls.pyに、アプリのルーティングを追加します。
urlpatterns = [
path('hellojson2/', include('hellojson2.urls')), # ←ここを追加
path('admin/', admin.site.urls),
]
4.動作確認
- webサーバ起動
python manage.py runserver
- アプリにアクセスしてみます。
5.最後に
Slick Gridでここまでやろうと思ったら、pluginの追加やjavascriptの実装が複雑になりがちですが、ag-gridは実装も少なくてとってもいい感じです。大量データの表示も高速に表示できるので、私的にはag-gridかなと思いました。