【JQuery】Ajax で動的に登録・更新・削除した後に DataTables にデータを再表示する
おはようございます。
昨日に引き続き、DataTables を利用したページの作成をしていきます。
今回はデータを登録・更新・削除した後、データを再取得して DataTables をリロードする方法を試してみます。
プログラムは前回のものを流用します。
【JQuery】DataTables に Ajax で動的に取得したデータを表示する
スポンサーリンク
画面の修正
主な変更点
1.CSSを外出し
2.検索用コントロールのスタイル変更
3.登録、更新用のモーダルダイアログ(Bootstrap)を追加
static/css/style.css
.container { border: 1px solid black; padding: 1rem; margin: 1rem; } .form-inline .label { margin-top: 10px; } input { ime-mode: active; } input.ime-disabled { ime-mode: disabled; } input.ime-inactive { ime-mode: inactive } .table > tbody > tr.active > td, .table > tbody > tr.active > th { background-color: #e1f2fe; }
templates/index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>DataTableサンプル</title> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/dataTables.bootstrap.min.css"/> <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/> <link rel="stylesheet" href="{{ static_url('css/style.css') }}"/> </head> <body> <div class="container container-fluid"> <div> 名前: <input type="text" class="input-sm ime-disabled" id="searchName" placeholder="名前" required> <button id="searchButton" type="button" class="btn btn-sm btn-info">検索</button> </div> <div> <table id="myTable" class="table table-striped tagle-bordered"> <thead> <tr> <th>No</th> <th>名前</th> <th>性別</th> <th>年齢</th> <th>種別</th> <th>好物</th> </tr> </thead> <tbody> </tbody> <tfoot> <tr> <th>No</th> <th>名前</th> <th>性別</th> <th>年齢</th> <th>種別</th> <th>好物</th> </tr> </tfoot> </table> </div> <div> <hr> <div class="pull-left"> <button id="registButton" type="button" class="btn btn-primary">登録</button> <button id="updateButton" type="button" class="btn btn-success" disabled>更新</button> </div> <div class="pull-right"> <button id="deleteButton" type="button" class="btn btn-danger" disabled>削除</button> </div> </div> </div> <div id="form" class="modal fade" tabindex="-1"> <div class="modal-dialog modal-dialog-centered "> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h4 id="dialogTitle" class="modal-title">登録</h4> </div> <div class="modal-body"> <div class="container-fluid"> <div class="row"> <div class="col-xs-12"> <form class="form-horizontal"> <div class="form-group"> <div class="form-inline"> <div class="col-sm-4"> <label class="control-label">No</label> <span class="label label-danger pull-right">必須</span> </div> <div class="input-group col-sm-2"> <input type="text" class="form-control ime-disabled" id="inputNo" placeholder="No" required> </div> </div> </div> <div class="form-group"> <div class="form-inline"> <div class="col-sm-4"> <label class="control-label">名前</label> <span class="label label-danger pull-right">必須</span> </div> <div class="input-group col-sm-2"> <input type="text" class="form-control" id="inputName" placeholder="名前" required> </div> </div> </div> <div class="form-group"> <div class="form-inline"> <div class="col-sm-4"> <label class="control-label">性別</label> <span class="label label-danger pull-right">必須</span> </div> <div class="input-group col-sm-2"> <input type="text" class="form-control" id="inputSex" placeholder="性別"> </div> </div> </div> <div class="form-group"> <div class="form-inline"> <div class="col-sm-4"> <label class="control-label">年齢</label> <span class="label label-danger pull-right">必須</span> </div> <div class="input-group col-sm-2"> <input type="text" class="form-control ime-disabled" id="inputAge" placeholder="年齢"> </div> </div> </div> <div class="form-group"> <div class="form-inline"> <div class="col-sm-4"> <label class="control-label">種別</label> <span class="label label-danger pull-right">必須</span> </div> <div class="input-group col-sm-7"> <select id="inputKind" class="form-control" > <option value="01">キジトラ</option> <option value="02">長毛種(不明)</option> <option value="03">ミケ(っぽい)</option> <option value="04">サビ</option> <option value="09">その他</option> </select> </div> </div> </div> <div class="form-group"> <div class="form-inline"> <div class="col-sm-4"> <label class="control-label">好物</label> <span class="label label-success pull-right">任意</span> </div> <div class="input-group col-sm-7"> <input type="text" class="form-control" id="inputFavorite" placeholder="好物"> </div> </div> </div> </form> </div> </div> </div> </div> <div class="modal-footer"> <div id="inputError" class="pull-left" style="color:red; padding:5px;"></div> <button id="sendRegistButton" type="button" class="btn btn-primary"><i class="fa fa-check"></i> 登録</button> <button id="sendUpdateButton" type="button" class="btn btn-primary"><i class="fa fa-check"></i> 修正</button> <button type="button" class="btn btn-default" data-dismiss="modal"><i class="fa fa-remove"></i> 閉じる</button> </div> </div> </div> </div> </body> <script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.js"></script> <script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script> <script type="text/javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script> <script type="text/javascript" src="https://cdn.datatables.net/1.10.19/js/dataTables.bootstrap.min.js"></script> <script type="text/javascript" src="{{ static_url('js/script.js') }}"></script> </html>
プログラムの修正
クライアント
主な変更点
1.テーブル行クリック時に行のスタイル(クラス)を変更する
2.各種ボタン(登録・更新・削除)クリック時の処理を追加
3.ダイアログの各種ボタン(登録・更新)クリック時の処理(Ajax)を追加
static/js/script.js
$(document).ready( function () { $('#myTable').DataTable({ 'paging' : true, 'pageLength' : 5, 'lengthChange': false, 'searching' : true, 'ordering' : true, 'info' : true, 'autoWidth' : true, "scrollCollapse": true, 'scrollX' : true, 'scrollY' : '185px', 'tabIndex' : -1, 'order' : [[ 0, 'asc' ]], 'colReorder' : true, 'serverSide' : false, 'ajax' : { 'url' : '/init', 'type' : 'POST', 'data' : function ( d ) { d.searchName = $("#searchName").val(); } }, 'columns' : [ { 'data' : 'no', width: 40 }, { 'data' : 'name', width: 60 }, { 'data' : 'sex', width: 40 }, { 'data' : 'age', width: 40 }, { 'data' : 'kind_name', width: 100 }, { 'data' : 'favorite', width: 120 }, ], 'language' : { 'decimal': ".", 'emptyTable': "表示するデータがありません。", 'info': "_START_ ~ _END_ / _TOTAL_ 件中", 'infoEmpty': "0 ~ 0 / 0 件", 'infoFiltered': "(合計 _MAX_ 件からフィルタリングしています)", 'infoPostFix': "", 'thousands': ",", 'lengthMenu': "1ページ _MENU_ 件を表示する", 'loadingRecords': "読み込み中...", 'processing': "処理中...", 'search': "絞り込み:", 'zeroRecords': "一致するデータが見つかりません。", 'paginate': { 'first': "最初", 'last': "最後", 'next': "次", 'previous': "前" } } }); // テーブル行クリックの設定 $('#myTable tbody').on("click", "tr", function() { if ($(this).find('.dataTables_empty').length == 0) { var owner = $(this); $("#myTable tr").removeClass("active"); owner.addClass("active"); $("#updateButton").prop("disabled", false); $("#deleteButton").prop("disabled", false); } }); // 検索ボタンクリック時の処理 $("#searchButton").on("click", function() { $('#myTable').DataTable().ajax.url("/search").load(); $('#myTable').DataTable().ajax.reload(); }); // $("#registButton").on("click", function() { // ダイアログ表示 $('#form').on('show.bs.modal', function (event) { // コントロール制御 $("#form #dialogTitle").text("新規登録"); $("#form #sendRegistButton").show(); $("#form #sendUpdateButton").hide(); $("#form #inputNo").prop("disabled", true); // フォーカス setTimeout(function(){ $("#inputNo").focus(); }, 500); }).modal("show"); }); $("#sendRegistButton").on("click", function(){ var param = { no : $("#inputNo").val() , name : $("#inputName").val() , sex : $("#inputSex").val() , age : $("#inputAge").val() , kind_cd : $("#inputKind").val() , favorite : $("#inputFavorite").val() } $.ajax({ url: "http://localhost:8080/regist", type: "POST", data: JSON.stringify(param), success: function(jsonResponse) { jsonResponse = jsonResponse.replace( /\\/g , "" ); var data = JSON.parse(jsonResponse); // テーブル更新 $('#myTable').DataTable().ajax.url("/search").load(); $('#myTable').DataTable().ajax.reload(); // フォームを閉じる $("#form").modal("hide"); }, error: function() { } }); }); $("#updateButton").on("click", function() { var selectedRows = $('#myTable').DataTable().rows('.active').data(); var param = { no : selectedRows[0].no } $.ajax({ url: "http://localhost:8080/getRecord", type: "POST", data: JSON.stringify(param), success: function(jsonResponse) { var data = JSON.parse(jsonResponse); var cat = data[0]; // ダイアログ表示 $('#form').on('show.bs.modal', function (event) { // 取得したデータのセット $("#inputNo").val(cat.no); $("#inputName").val(cat.name); $("#inputSex").val(cat.sex); $("#inputAge").val(cat.age); $("#inputKind").val(cat.kind_cd); $("#inputFavorite").val(cat.favorite); // コントロール制御 $("#form #dialogTitle").text("更新"); $("#form #sendRegistButton").hide(); $("#form #sendUpdateButton").show(); $("#form #inputNo").prop("disabled", true); setTimeout(function(){ $("#inputName").focus(); }, 500); }).modal("show"); }, error: function() { } }); }); $("#sendUpdateButton").on("click", function(){ var param = { no : $("#inputNo").val() , name : $("#inputName").val() , sex : $("#inputSex").val() , age : $("#inputAge").val() , kind_cd : $("#inputKind").val() , favorite : $("#inputFavorite").val() } $.ajax({ url: "http://localhost:8080/update", type: "POST", data: JSON.stringify(param), success: function(jsonResponse) { jsonResponse = jsonResponse.replace( /\\/g , "" ); var data = JSON.parse(jsonResponse); // テーブル更新 $('#myTable').DataTable().ajax.url("/search").load(); $('#myTable').DataTable().ajax.reload(); // フォームを閉じる $("#form").modal("hide"); }, error: function() { } }); }); $("#deleteButton").on("click", function(){ var selectedRows = $('#myTable').DataTable().rows('.active').data(); var param = { no : selectedRows[0].no } $.ajax({ url: "http://localhost:8080/delete", type: "POST", data: JSON.stringify(param), success: function(jsonResponse) { // テーブル更新 $('#myTable').DataTable().ajax.url("/search").load(); $('#myTable').DataTable().ajax.reload(); }, error: function() { } }); }); });
サーバー
主な変更点
1.MySQLUtilクラスを追加してデータ操作の処理をまとめる
2.登録・更新・削除に対応するメソッドの追加
MySQLUtil.py
import mysql.connector import logging from contextlib import closing class MySQLUtil: """ MySQL 操作用クラス """ def __init__(self, host="localhost", port="3306", user="USER01", password="USER01", database="DB01"): self.config = { "host": host, "port": port, "user": user, "password": password, "database": database } def insert_data(self, data): """ データを登録します :param data: :return: """ with closing(mysql.connector.connect(**self.config)) as conn: c = conn.cursor() # データ登録 sql = "INSERT INTO TBLCAT VALUES (%s,%s,%s,%s,%s,%s)" c.execute(sql, data) c.close() conn.commit() def update_data(self, data): """ データを更新します :param data: :return: """ with closing(mysql.connector.connect(**self.config)) as conn: c = conn.cursor() # データ登録 sql = "UPDATE TBLCAT SET NAME = %s, " \ " SEX = %s, " \ " AGE = %s, " \ " KIND_CD = %s, " \ " FAVORITE = %s " \ "WHERE " \ " NO = %s" c.execute(sql, data) c.close() conn.commit() def delete_data(self, no): """ データを削除します :return: """ logging.info("delete_data") with closing(mysql.connector.connect(**self.config)) as conn: c = conn.cursor() # データクリア sql = "DELETE FROM TBLCAT WHERE NO = '" + no + "'" c.execute(sql) c.close() conn.commit() def get_data(self, no="", search_name=""): """ データを取得します :return: """ result = [] with closing(mysql.connector.connect(**self.config)) as conn: c = conn.cursor(dictionary=True) # SQL組み立て sql = "SELECT C.NO, C.NAME, C.SEX, C.AGE, C.KIND_CD, K.KIND_NAME, C.FAVORITE FROM TBLCAT C" sql += " LEFT OUTER JOIN MSTKIND K ON ( C.KIND_CD = K.KIND_CD)" if no != "": sql += " WHERE NO = '" + no + "'" else: sql += " WHERE C.NAME LIKE '" + search_name + "%'" sql += " ORDER BY NO" c.execute(sql) for r in c.fetchall(): result.append({ "no": r['NO'], "name": r['NAME'], "sex": r['SEX'], "age": r['AGE'], "kind_cd": r['KIND_CD'], "kind_name": r['KIND_NAME'], "favorite": r['FAVORITE'], }) return result def get_next_no(self): """ ユーザー毎にカレンダーIDの最大値+1を返します :return: """ result = 0 with closing(mysql.connector.connect(**self.config)) as conn: c = conn.cursor(dictionary=True) sql = "SELECT MAX(NO) + 1 AS NO FROM TBLCAT" c.execute(sql) result = c.fetchone() return result[r"NO"]
Main.py
import json import logging import os import tornado.ioloop import mysql.connector from tornado.web import RequestHandler from tornado.options import options from contextlib import closing from Utils.MySQLUtil import MySQLUtil class MainHandler(RequestHandler): """ 画面表示 """ def get(self): logging.info("MainHandler [get]") self.render("index.html") class InitHandler(RequestHandler): """ 一覧初期化用 """ def post(self): list = [] result = { 'data': list } self.write(json.dumps(result, ensure_ascii=False)) class SearchHandler(RequestHandler): """ データ検索 """ def initialize(self): logging.info("SearchHandler [initialize]") def post(self): """ データをJSON形式で返します :return: """ logging.info("SearchHandler [post]") search_name = self.get_argument("searchName") mysql = MySQLUtil() data_list = mysql.get_data(search_name=search_name) result = { 'data': data_list } self.write(json.dumps(result, ensure_ascii=False)) class GetRecordHandler(RequestHandler): """ プライマリキーを指定してデータ取得 """ def initialize(self): logging.info("GetRecordHandler [initialize]") def post(self): logging.info("GetRecordHandler [post]") param = json.loads(self.request.body) no = str(param["no"]) mysql = MySQLUtil() result = mysql.get_data(no=no) self.write(json.dumps(result, ensure_ascii=False)) class RegistHandler(RequestHandler): """ データ登録 """ def initialize(self): logging.info("RegistHandler [initialize]") def post(self): logging.info("RegistHandler [post]") mysql = MySQLUtil() param = json.loads(self.request.body) no = mysql.get_next_no() data = [ no, param["name"], param["sex"], param["age"], param["kind_cd"], param["favorite"], ] mysql.insert_data(data) result = { 'result': "success" } self.write(json.dumps(result, ensure_ascii=False)) class UpdateHandler(RequestHandler): """ 更新 """ def initialize(self): logging.info("UpdateHandler [initialize]") def post(self): logging.info("UpdateHandler [post]") mysql = MySQLUtil() param = json.loads(self.request.body) data = [ param["name"], param["sex"], param["age"], param["kind_cd"], param["favorite"], param["no"], ] mysql.update_data(data) result = { 'result': "success" } self.write(json.dumps(result, ensure_ascii=False)) class DeleteHandler(RequestHandler): """ 削除 """ def initialize(self): logging.info("RegistHandler [initialize]") def post(self): logging.info("RegistHandler [post]") mysql = MySQLUtil() param = json.loads(self.request.body) no = str(param["no"]) mysql.delete_data(no) result = { 'result': "success" } self.write(json.dumps(result, ensure_ascii=False)) app = tornado.web.Application([ (r"/", MainHandler), (r"/init", InitHandler), (r"/search", SearchHandler), (r"/getRecord", GetRecordHandler), (r"/regist", RegistHandler), (r"/update", UpdateHandler), (r"/delete", DeleteHandler), ], template_path=os.path.join(os.getcwd(), "templates"), static_path=os.path.join(os.getcwd(), "static"), ) if __name__ == "__main__": options.parse_command_line() app.listen(8080) logging.info("server started") tornado.ioloop.IOLoop.instance().start()
操作してみる
検索
登録
親画面の「登録」ボタンをクリックで表示。
内容を入力してダイアログの「登録」ボタンをクリック。
No5にデータが追加されました。ちゃんと件数表示も更新されています。
更新
親画面で No5 を選択後「更新」ボタンをクリックで表示。内容は一度DBに問い合わせてフォームにセットしています。
種別を変更して「修正」ボタンをクリック。
一覧の種別も変更されました。
削除
No5 を選択して「削除」ボタンをクリック。
No5 が一覧から削除されました。
まとめ
ちょっと長くなってしまいましたが、一通り、DataTables と サーバーとの通信はできましたね。
ここまで出来れば、ちょっとしたシステムにも組み込むことができそうです。
DataTablesはまだまだ色々出来そうなので、また時間が出来たら試してみたいと思います。
ではでは。
ディスカッション
コメント一覧
まだ、コメントがありません