tabulatorを使って、データベースの更新にトライ(python版）

「tabulatorを使って、データベースの更新にトライ」を年初に投稿して、久々にDEMOサイトにアクセスしてみたら、動かなくなっていました。そこで最近はpythonでプロトタイプシステムを作成していたので、そちらで再掲します。クライアントの作りはjssp-rpcをajaxに書き換えただけです。Python、Flaskについてはいろいろ紹介されているので、ここでは触れません。

# ローカル環境
1. Windows-x86Windows10HOME
2. Anaconda3-5.2.0  ライブラリは以下を利用
3. Flask
4. SQLite
5. クライアントライブラリ tabulator

wwwroot
 |-main.py
 |-test.db
 |-templatesフォルダにhtmlを配置
 |-staticフォルダにCDNで取得できないライブラリを配置

# 公開環境
[こちら](https://qiita.com/vye03702/items/cdb0d92c032281fe384e)で紹介している拡張したpythonにライブラリを追加する方法で再構築しました。

<del>Azureで動作させるにあったっては、結構苦労しました。ローカルで仮想環境(env)で動作させてgitでアップする例がMSのサイトでも紹介されていますが、site-packagesのライブラリを読み込めず動かす事ができませんでした。
そこで、D:\home\site\wwwroot配下にフォルダを作成し、そこにsite-packagesをftpでアップロードし、プログラムにsys.path.append('D:/home/site/wwwroot/Lib/site-packages')でロード場所を追加してライブラリを読み込むようにしました。Anacondaのsite-packagesは量が多いので、Azure WebAppsのバージョンと同じ3.4のpythonのsite-packagesにFlaskをpipで追加したものをアップしています。</del>


###[demo→2](https://sl-vin.azurewebsites.net/) 4行目の2「Tablatorを使ってDBのグリッド表示、登録を行う」
※7/12環境設定を拡張してライブラリを追加する方法で環境を再構築
メニューよりTablatorを使ってDBのグリッド表示、登録を行うをクリック。
表示するのはLoad、追加の場合はadd-rowで新規行を表示し入力してExcute、修正・削除は右端をupd/delを選択してExcute。

# ソース
```tabDB.html
{% extends "base.html" %}
{% block body %}

<div class="card" ; >
<div class="card-header" style="height:50px; font-size:1.5rem; ">
    <b>Tablatorを使ってDBのグリッド表示、登録を行う</b>
</div>
<div class="card-body">
	<div class="row">
		<div class="col-md-2 mb-1">
			<input type="text" class="form-control" id="input-text" value="%">
		</div>
		<div class="col-md-2 mb-1">
			<button class="btn btn-default" id="button" style="width: 100px; padding: 5px;">Load Data</button>
		</div>
		<div class="col-md-2 mb-1">市Autocomplete</div>
		<div class="col-md-2 mb-1">
			<input type="text" class="form-control" id="tags">
		</div>
		<div class="col-md-2 mb-1">
			<button class="btn btn-default" id="excute_button" style="width: 100px; padding: 5px;">Excute</button>
		</div>
		<div class="col-md-2 mb-1">
			<button class="btn btn-default" id="add-row" style="width: 100px; padding: 5px;">Add-row</button>
		</div>
	</div>
</div>  <!--class="card-body"の終端-->
</div>  <!--class="card"の終端-->

<div id="example-table"></div>

<script type="text/javascript">
"use strict";
// **********************************************
// autoコンプリート
// **********************************************
$(function() {
   $("#tags").autocomplete({
	  source: function(request, response) {
		  $.ajax({
			 url: "/city",
			 dataType: "json",
			 type: "POST",
			 cache: false,
			 data: { param: request.term },
			 success: function(data) {
				response(JSON.parse(data.city));
			 },
			 error: function(XMLHttpRequest, textStatus, errorThrown) {
				 response(['']);
			 }
		  });
	  }
   });
});

// **********************************************
// 
// **********************************************
$("#example-table").tabulator({
	height:400, // set height of table (in CSS or here), this enables the Virtual DOM and improves render speed dramatically (can be any valid css height value)
	layout:"fitColumns", //fit columns to width of table (optional)
    pagination:"local",
    paginationSize:10,
	columns:[ //Define Table Columns
		{title:"Id", field:"id", width:60},
		{title:"Name", field:"name", editor:"input", width:150},
		{title:"Age", field:"age", editor:"input", width:80},
		{title:"Adress", field:"adress", editor:"input", width:150},
		{title:"Code", field:"code", editor:"input", width:80},
		{title:"Date Of Birth", field:"dob", editor:"input", width:80},
		{title:"Male/Female", field:"mf",editor:"select", editorParams:{"male":"Male", "female":"Female"}, width:80},
		{title:"Upd/Del", field:"ud",editor:"select", editorParams:{"":"", "Add":"Add","Upd":"Upd", "Del":"Del"}, width:80},
		],

});

//新規行追加
$("#add-row").click(function(){
    $("#example-table").tabulator("addRow", {ud:"Add"});
});

$(window).load(init());
function init() {

//ロード
	$("#button").click(function() {
		var textData = JSON.stringify({"text":$("#input-text").val()});
		$.ajax({
			type:'POST',
			url:'/tabPost',
			data:textData,
			contentType:'application/json',
			success:function(data) {
				var tabledata = JSON.parse(data.ResultSet);
				$("#example-table").tabulator("setData",tabledata );
			}
		});
	return false;
	});
	
//実行
	$("#excute_button").click(function() {
		var data = $("#example-table").tabulator("getData");
		for(var i = 0; i < data.length; i++) {
		switch( data[i].ud ){
			case 'Add':
				var textData = JSON.stringify({"add_name":data[i].name,
											   "add_age":data[i].age,
											   "add_adress":data[i].adress,
											   "add_code":data[i].code,});
				$.ajax({
				  type:'POST',
				  url:'/tabAdd',
				  data:textData,
				  contentType:'application/json',
				  success:function(data) {
					alert("登録結果=" + data);
				  }
				});
				break;
			case 'Upd':
				alert("★Upd");
				var textData = JSON.stringify({"id":data[i].id,
											   "add_name":data[i].name,
											   "add_age":data[i].age,
											   "add_adress":data[i].adress,
											   "add_code":data[i].code,});
				$.ajax({
				  type:'POST',
				  url:'/tabUpd',
				  data:textData,
				  contentType:'application/json',
				  success:function(data) {
					alert("更新結果=" + data);
				  }
				});
				break;
			case 'Del':
				alert("★Upd");
				var textData = JSON.stringify({"id":data[i].id})
				$.ajax({
				  type:'POST',
				  url:'/tabDel',
				  data:textData,
				  contentType:'application/json',
				  success:function(data) {
					alert("削除結果=" + data);
				  }
				});
                break;
			default:
                break;
		}
		}
		return false;
	});
}

</script>
{% endblock %}
```
```python:main.py
#**************************************
# プロト
#**************************************
import os
import sys
import platform
import sqlite3

#sys.path.append('D:/home/site/wwwroot/Lib/site-packages') ←不要になりました。
from flask import Flask, render_template, request, jsonify, send_file, make_response, send_from_directory, request, jsonify, session, redirect, url_for
import json
import werkzeug
import datetime

#import openpyxl
#import pyodbc
#from ldap3 import Server, Connection

sqlite_DB = './test.db' 

application = Flask(__name__)
application.config.from_object(__name__)

#**************************************
# 共通処理：SQLiteの戻り値が単純な配列なのでそれを辞書型にする定義
#**************************************
def dict_factory(cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

#************************************** 
#  オートコンプリート
#************************************** 
@application.route("/city",methods=['POST'])
def city_tags():
    result = []
    db_key = [request.form['param'] + "%"]

    connection = sqlite3.connect(sqlite_DB)
    cursor = connection.cursor()

    for row in cursor.execute('select city_name from city where city_name like ?',db_key):
        result.append(row[0])
        print(row[0])

    cursor.close()
    connection.close()
    return jsonify(city = json.dumps(result))

#************************************** 
#  ルーティング
#************************************** 

#初期メニュー
@application.route('/')
def index():
    return render_template('index.html',msg='login')


@application.route('/test', methods=['POST'])
def test():
    return render_template('TEST.html')

#tabltorグリッド
@application.route('/tabDB',methods=['POST'])
def tabDB():   
    return render_template('tabDB.html')

#Bootstrap画面装飾
@application.route('/Bootstrap',methods=['POST'])
def Bootstrap():

    try: 
        if  request.form['name'] == "":  #値が空文字の場合
            key = "%"
        else:                            #値がセットされている場合
            key = request.form['name'] + "%"
    except:
        key = "%"                        #例外処理  他の画面から呼ばれた場合

    db_key = [key]     #配列にして渡している

    connection = sqlite3.connect(sqlite_DB)
    connection.row_factory = dict_factory
    cursor = connection.cursor()

    cursor.execute('select * from test where name like ?',db_key)

    result = cursor.fetchall()
    cursor.close()
    connection.close()
    return render_template('Bootstrap.html',result=result)

#**************************************
#tabltorのデータ取得
#**************************************
@application.route('/tabPost', methods=['POST'])
def lower_conversion():
    text = request.json['text']

    connection = sqlite3.connect(sqlite_DB)
    connection.row_factory = dict_factory
    cursor = connection.cursor()
    key = [text]                     #配列にして渡している
    cursor.execute('select * from test where code like ?',key)
    result = cursor.fetchall()
    cursor.close()
    connection.close()
    return jsonify(ResultSet=json.dumps(result))  #json.dumpsで辞書型をjson形式に変換した上で、
#jsonifyでResultSet=〇〇→ResultSet：〇〇に変換している。Content-Typeをapplication/json に設定して Response を返してくれる。

#**************************************
#tabltorの登録
#**************************************
@application.route('/tabAdd', methods=['POST'])
def tabAdd():
    connection = sqlite3.connect(sqlite_DB)
    data = [None,request.json['add_name'],int(request.json['add_age']),request.json['add_adress'],request.json['add_code'],'','']
    try:
        connection.execute('INSERT INTO test VALUES (?, ?, ?, ?, ?, ?, ?)', data)
        connection.commit()
        connection.close()
        return "success"
    except sqlite3.Error as e:
        print('sqlite3.Error occurred:', e.args[0])
        connection.close()
        return "error" + e.args[0]

#**************************************
#tabltorの更新
#**************************************
@application.route('/tabUpd', methods=['POST'])
def tabUpd():
    connection = sqlite3.connect(sqlite_DB)
    data = [request.json['add_name'],
            int(request.json['add_age']),
            request.json['add_adress'],
            request.json['add_code'],'','',
            request.json['id']]


    try:
        connection.execute('update test set name = ?, age = ?, adress = ?, code = ?, dateofbirth = ?, male_female = ? where id =?', data)
        connection.commit()
        connection.close()
        return "success"
    except sqlite3.Error as e:
        print('sqlite3.Error occurred:', e.args[0])
        connection.close()
        return "error" + e.args[0]

#**************************************
#tabltorの削除
#**************************************
@application.route('/tabDel', methods=['POST'])
def tabDel():
    connection = sqlite3.connect(sqlite_DB)
    data = [request.json['id']]

    try:
        connection.execute('DELETE  FROM  test where id =?', data)
        connection.commit()
        connection.close()
        return "success"
    except sqlite3.Error as e:
        print('sqlite3.Error occurred:', e.args[0])
        connection.close()
        return "error" + e.args[0]

#************************************** 
#  テストサーバ起動
#************************************** 
if __name__ == '__main__':
	application.debug = True # デバッグ
	application.run(host='0.0.0.0', port=8000)```
```
```sql:クリエイトsql
CREATE TABLE test (
    id               INTEGER PRIMARY KEY AUTOINCREMENT,
    name             text    DEFAULT '',
    age              integer DEFAULT zero,
    adress           text    DEFAULT '',
    code             text    DEFAULT '',
    dateofbirth      text     DEFAULT '',
    male_female      text     DEFAULT ''
);
```
web.config、base.html、common.pyは[こちら](https://qiita.com/vye03702/items/b62d845d520a47da50de)をご覧ください。

とりあえずTablatorでDBアクセスをやろうとしている方のご参考に取り急ぎ再掲します。
時間を見つけて他のプログラムを紹介できればと考えています。