python Bottleの基本10 編集機能付きテーブル

# このテーマは、編集機能付きテーブルをサクッと作るためのテンプレートです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274715/091f78c8-b76c-d843-80be-674318943b6c.png)

# [mindmup-editabletable.js](https://github.com/mindmup/editable-table)を利用して編集機能付きテーブルを実現しています。

```ex10.py
from bottle import *
import json,os
save=None
#HTML デコレーション(Bootstrap4)
def Html(title):
    html='''
        <!DOCTYPE html>
            <html lang="ja">
            <head>
                <meta charset="utf-8" />
                <title>%s</title>
                <link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
	            <link href="static/content/jumbotron.css" rel="stylesheet" />
                <link rel="stylesheet" type="text/css" href="/static/content/site.css" />
                <script src="/static/scripts/modernizr-2.6.2.js"></script>
                <script src="/static/scripts/jquery-1.10.2.min.js"></script>
                <script src="/static/scripts/bootstrap.min.js"></script>
                <script src="/static/scripts/respond.min.js"></script>
	            <script src="/static/scripts/mindmup-editabletable.js"></script>
                <script src="/static/scripts/jquery.redirect.js"></script>
            </head>
            <body>
            %s          
            </body>
            </html>'''
    def f0(f):
        def f1(*a,**b):          
            return html%(title,f(*a,**b))
        return f1
    return f0
def Body():
    def f0(f):
        def f1(*a,**b):
            return '<div class="container body-content">%s</div>'%f(*a,**b)
        return f1
    return f0
def Navi(menu):
    nav='''<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
            <a class="navbar-brand" href="/">%s</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarsExampleDefault">
            <ul class="navbar-nav mr-auto">
                %s
            </ul>
            <form class="form-inline my-2 my-lg-0">
                <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
                <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
            </form>
            </div>
     </nav>%s'''
    def f0(f):
        def f1(*a,**b):
            nm=''.join(['<li class="nav-item"><a class="nav-link" href="/%s">%s</a></li>'%(x,x) for x in menu[1:]])
            return nav%(menu[0],nm,f(*a,**b))
        return f1
    return f0
def routes(menu):
    def f0(f):
        route('/','GET',f)
        [route("/%s"%x,'GET',f) for x in menu]
        def f1(*a,**b):
            return f(*a,**b)
        return f1
    return f0
#JOMBOTRON デコレーション
def Jumbotron(title,paragraph):
    jumbo='''<div class="jumbotron">
        <div class="container">
          <h1 class="display-3">%s</h1>
          <p>%s</p>
        </div>
      </div>%s'''
    def f0(f):
        def f1(*a,**b):
            return jumbo%(title,paragraph,f(*a,**b))
        return f1
    return f0
#script デコレーション
def script(js):
    def f0(f):
        def f1(*a,**b):
            return f(*a,**b)+js
        return f1
    return f0
# string csv data to dict
def csv2js(d):
    dx=[x.split(',') for x in d.splitlines()]
    return [{ a:b for a,b in zip(dx[0],x)}for x in dx[1:]]
# Table デコレーション
def table(dx):
    head='<tr>'+''.join(['<th scope="col" class="text-nowrap">%s</th>'%x for x in dx[0]])+'</tr>'
    body=''.join(['<tr>'+''.join(['<td scope="col" class="text-nowrap">%s</td>'%x[a] for a in x])+'</tr>' for x in dx])
    def f0(f):
        def f1(*a,**b):
            return '<table style="margin-top:1em" class="table table-sm table-bordered table-striped table-responsive-sm table-hover">%s%s</table>%s'%(head,body,f(*a,**b))
        return f1
    return f0
def table_(dx):
    head='<tr>'+''.join(['<th scope="col" class="text-nowrap">%s</th>'%x for x in dx[0]])+'</tr>'
    body=''.join(['<tr>'+''.join(['<td scope="col" class="text-nowrap">%s</td>'%x[a] for a in x])+'</tr>' for x in dx])
    return '<table style="margin-top:1em" class="table table-sm table-bordered table-striped table-responsive-sm table-hover">%s%s</table>'%(head,body)

adr='''ID,名前,性別,血液型,生年月日,電話番号,携帯番号,メール,郵便番号,住所
1,関 波子,女,AB,1980/7/31,03-3060-4716,090-7787-3784,sk@eaccess.net,135-0034,東京都江東区永代8-1-4
2,小倉 準司,男,A,1973/10/8,0166-36-3522,,junzi-kokura@eaccess.net,071-1544,北海道上川郡東神楽町１４号6-13-1
3,西村 有紀子,女,O,1972/12/5,0745-67-2723,090-5165-2074,okikuy1972@livedoor.com,635-0805,奈良県北葛城郡広陵町萱野5-2-7 ヴェルテックス萱野 1009
4,谷 僧三郎,男,B,1989/1/17,077-323-7124,090-3781-1181,suzbrutn@dsn.ad.jp,520-0011,滋賀県大津市南志賀2868
5,山口 和久,男,A,1973/2/21,0183-21-6828,070-4790-1232,kazuhisa73@geocities.com,012-0813,秋田県湯沢市前森1-11-7
6,米田 一生,男,O,1984/7/25,043-883-8543,,issei07@dsn.ad.jp,264-0029,千葉県千葉市若葉区桜木北4-13-4
7,村松 希美江,女,O,1980/11/21,075-727-1789,090-4849-6939,kimie.muramatu@odn.ne.jp,616-8151,京都府京都市右京区太秦帷子ヶ辻町6-6-7
8,小野寺 眞八,男,O,1986/4/28,0748-01-7199,090-1457-0772,ondr.sny@gmo-media.jp,527-0135,滋賀県東近江市横溝町6-6-7
9,志村 陽一郎,男,O,1975/7/8,0136-88-6603,,smr-yutru@dion.ne.jp,048-1321,北海道磯谷郡蘭越町湯里5-9-5 ル・メール湯里
10,鎌田 敏美,女,B,1993/6/9,0554-03-0156,090-5690-8749,kamata0609@example.com,409-0115,山梨県上野原市松留4631'''
js='''
<script>
             function getData(){
		        var d=[];$('table tr').each(function(i, e){
		         var dd=[]; if (i===0) $(this).find('th').each(function(j, el){dd.push($(this).text())}); 
					         else $(this).find('td').each(function(j, el){dd.push($(this).text())});
		         d.push(dd);
		        });return d;
            }
            $(function () {
             $('table').editableTableWidget();
	         $('#upload').click(function(){
	            var s=getData();
			        var k=s[0],d=[];for(var i=1;i<s.length;i++){
				        var x=s[i],dd={}; for(var j=0;j<x.length;j++) dd[k[j]]=x[j];
				        d.push(dd);
			        }
               $.redirect('/upload',{js:JSON.stringify(d)});
  	         });
            });
           </script>
'''
save=csv2js(adr)
menu='Home,Table'.split(',')

@routes(menu)
@Html('Hello')
@Navi(menu)
@Jumbotron('Table テンプレート','これさえあればサクッと作ることができます')
@Body()
@script(js)
def Home():
    global save
    p=request.urlparts[2]
    if p=='/Table' or p=='/' :
        return table_(save)+"<button class='btn btn-primary' id='upload'>保存</button>"
    return "<h1>%s</h1><h1>Hello World</h1>"%(p)

@route('/upload',method="POST")
@Html('Hello')
@Jumbotron('Form表示テンプレート','これさえあればサクッと作ることができます')
@Navi(menu)
@Body()
def upload():
    global save
    save=eval(request.params.decode().js)
    return table_(save)+"<button class='btn btn-primary' id='upload'>保存</button>"
#faviconの読み込み    
@route('/favicon.ico')
def favcon():
    return static_file('favicon.ico', root='./static')
#staic ファイルの読み込み
@route('/static/<filepath:path>')
def server_static(filepath):
    return static_file(filepath, root='./static')
#web server のhost portの設定
HOST,PORT='localhost',8080
if __name__ =='__main__':
    run(host=HOST,port=PORT)
```
###table関数は、デコレーション用です。table_は、html生成用です。
### scriptデコレーションを作り,jsを定義しています。
```snippet.js
<script>
    function getData(){
	 var d=[];$('table tr').each(function(i, e){
		var dd=[]; if (i===0) $(this).find('th').each(function(j, el){dd.push($(this).text())}); 
					else $(this).find('td').each(function(j, el){dd.push($(this).text())});
		d.push(dd);
	 });return d;
    }
    $(function () {
        $('table').editableTableWidget();
	    $('#upload').click(function(){
	    var s=getData();
		var k=s[0],d=[];for(var i=1;i<s.length;i++){
			var x=s[i],dd={}; for(var j=0;j<x.length;j++) dd[k[j]]=x[j];
			d.push(dd);
		}
        $.redirect('/upload',{js:JSON.stringify(d)});
  	    });
    });
</script>
```` 
##### function getData()は、テーブルからデータを剥ぎ取る関数です。
#####$.redirect('/upload',{js:JSON.stringify(d)});によって/uploadにデータをPOSTしています。この関数は、通常は、$.postとして使いますが画面を新しく遷移するときに使います。
下記のjqueryを導入することにより利用可能です。

```
<script src="/static/scripts/jquery.redirect.js"></script>
```
[jquery.redirect](https://github.com/mgalante/jquery.redirect)は、ここにあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274715/dfbdcf19-4a06-620b-f514-430a51a7053a.png)