稼働中

(v_15)PythonのフレームワークBottleを試す(1)

Bottleで始めるWEBアプリ

Pythonで使えるWebフレームワークのBottleを少し学習してみました。
何かに使ってみようと思いましたが思い浮かびませんでしたので、「bottleで始めるWEBアプリの最初の一歩「https://www.slideshare.net/satoshiyamada71697/bottleweb」を参考に試してみました。

具体的には、名前と年齢のデータベースをブラウザで以下のように名前と年齢のデータ一覧を表示、新規登録、「変更、削除」の操作ができる感じにしました。
データベースも扱うのでsqlite3も使っています。
SQL文の書き方は「https://www.sejuku.net/blog/104588」を参考にしました。
データ一覧表示

データベース

名前と年齢のデータベースitems.dbをsqlite3を使って作成します。
Thonnyのshellで確認しながら進めます。


#sqlite3をインポート
>>> import sqlite3
#データベースitems.dbを接続します。無ければ作成される
>>> con=sqlite3.connect('items.db') 
>>> type(con)
<class 'sqlite3.Connection'>

#データベース操作のためCursorオブジェクトを作成
>>> cur=con.cursor()
>>> type(cur)
<class 'sqlite3.Cursor'>

#カラム id,name,ageのテーブルを作成
>>> sql='CREATE TABLE items(id,name,age)'
>>> cur.execute(sql)
<sqlite3.Cursor object at 0x03C64D20>

#SQL文 INSERT INTO~VALUES~でテーブルにデータ追加
>>> sql="INSERT INTO items VALUES (1,'yamada',30)"
>>> cur.execute(sql)
<sqlite3.Cursor object at 0x03C64D20>

#SQL文 SELECT~FROM~でテーブルからデータ取り出し、fetchallで表示
>>> b=cur.execute("SELECT * FROM items")
>>> b.fetchall()
[(1, 'yamada', 30)]

#executemanyを使ってテーブルに複数行のデータ追加する
>>> data=[
    (2, 'sato',22),
    (3, 'kawano',33),
]
>>> sql="INSERT INTO items VALUES (?, ?, ?)"
>>> cur.executemany(sql,data)
<sqlite3.Cursor object at 0x03C64D20>

>>> b=cur.execute("SELECT * FROM items")
>>> b.fetchall()
[(1, 'yamada', 30), (2, 'sato', 22), (3, 'kawano', 33)]

#確定する
>>> con.commit()
#接続を閉じる
>>> con.close()

以上を整理してまとめています。


web_sql_01b.py
!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sqlite3

#sqlite3.Connection
con=sqlite3.connect('items.db')
#sqlite3.Connection
cur=con.cursor()
#table
cur.execute('CREATE TABLE items(id,name,age)')
data = [
    (1, 'yamada',30),
    (2, 'sato',22),
    (3, 'kawano',33),
]
#executemany テーブルに複数行のデータ追加(レコード登録)を行う
cur.executemany("INSERT INTO items VALUES(?, ?, ?)", data)
# 確定
con.commit()

#全カラムデータを選択
b=cur.execute("SELECT * FROM items")
#データ表示
print(b.fetchall())
con.close()

実行結果


>>> %Run web_sql_01b.py
[(1, 'yamada', 30), (2, 'sato', 22), (3, 'kawano', 33)]

データ表示 /list

「http://localhost:8080/list」でデータリスト表示されるようにします。
その前に、HTMLのテンプレートファイルを使って「http://localhost:8080/temp」で以下が表示されるようにしてみます。
データ表示ページ
以下のテンプレートファイル「temp_01.tpl」を作成してワークフォルダーに保存します。


temp_01.tpl
<!DOCTYPE html>
<html>
<h3 style="color:red;">Python Bottle SQLite3</h3>
<p style="color:blue;">テンプレートのテスト</p>
</html>
</code></pre>

Thonnyのshellで確認しながら進めます。


#bottle.pyからroute, run, templateをimport
>>> from bottle import route, run, template

#/tempでテンプレートを表示
>>> @route('/temp')
    def test():
        return template('temp_01')

#localhostを起動
>>> run(host='localhost', port=8080, debug=True)
Bottle v0.13-dev server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

C:\****\****\****\bottle.py:4088: DeprecationWarning: Flags not 
at the start of the expression '\\{\\{((?:(?mx)(      ' (truncated)
  patterns = [re.compile(p % pattern_vars) for p in patterns]
127.0.0.1 - - [**/May/2024 16:44:46] "GET / HTTP/1.1" 404 726
127.0.0.1 - - [**/May/2024 16:44:58] "GET /temp HTTP/1.1" 200 146

「http://localhost:8080/temp」で表示されます。
ちなみに「http://127.0.0.1:8080/temp」でもよいです。
「Ctrl-C」で終了します。
データ表示ページ

Pythonコード埋込

HTML内にPythonコードを記載して処理するには、行を「%」でマークし、「%end」で明示的に閉じます。
HTMLのテンプレートファイル「temp_02.tpl」を以下のようにしました。
今日の日付と/tempで返される値を足し算して結果を表示します。


temp_02.tpl
<!DOCTYPE html>
<html>
<h3 style="color:red;">Python Bottle SQLite3</h3>
<p style="color:blue;">テンプレートのテスト</p>
%import datetime
%today=datetime.date.today()
%d1=data1
%d2=data2
%wa=d1+d2
%end
<p style="color:green;">今日は{{today}}です</p>
<p style="color:brown; Bold;"><strong>{{d1}}+{{d2}}={{wa}}</strong> です</p>
</html>

/tempでtemp_02.tplを表示するように以下のようにしています。
a=10、b=20の値をtemp_02.tplに返しています。
/tempでtemp_02.tplを表示するように以下のようにしています。
a=10、b=20の値をtemp_02.tplに返しています。


test_tmp_02b.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from bottle import route, run, template
@route('/temp')
def test():
    a=10
    b=20
    return template('temp_02',data1=a,data2=b)
run(host='localhost', port=8080, debug=True)

実行結果
今日の日付と計算結果が表示されました。
Pythonコード埋め込み

データ表示 /list

「http://localhost:8080/list」でデータリスト表示されるようにします。
テンプレートにlist_tmp3.tplを使っています。
※新規登録、削除、変更も表示されていますが、ここでは関係ないです。


テンプレート  list_tmp3.tpl
<!DOCTYPE html>
<html>
<h3>アイテム一覧</h3>
<a href='/add'>新規登録</a>
<table border='1'>
<tr>
<td>id</td>
<td>Name</td>
<td>Age</td>
<td>mod</td>
<td>del</td>
</tr>
%item_list=data
%for item in item_list:
<tr>
<td>{{item['id']}}</td>
<td>{{item['name']}}</td>
<td>{{item['age']}}</td>
<td><a href="/mod/{{item['id']}}">変更</a></td>
<td><a href="/del/{{item['id']}}">削除</a></td>
</tr>
%end
</table>
</html>

部分説明
Pythonコードを埋め込みfor文で各行を繰り返しで表示しています。


スクリプト   web_lst_01b.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from bottle import route, run, template
import sqlite3

@route('/list')
def db_list():
    con=sqlite3.connect('items.db')
    cur=con.cursor()
    #ASC 昇順 DESC 降順
    c=cur.execute("SELECT * FROM items ORDER BY id ASC")
    
    item_list=[]
    for row in c.fetchall():
        item_list.append({"id":row[0],"name":row[1],"age":row[2]})
    con.close()
    
    # list_tmp3.tpl に渡す引数名 data
    return template('list_tmp3',data=item_list)

run(host='localhost', port=8080, debug=True)

部分説明
下方のHTMLのテンプレートファイル list_tmp3.tplを使って表示しています。
SQL文のSELECT~FROM~でテーブルからデータ取り出しする際に
ORDER BY id ASC
を追加してidの昇順で並べ替えています。
ちなみにASCをDESCにすると降順になります。
c.fetchall()で読み出し、以下のようなitem_listリストにしてlist_tmp3.tplに返しています。
先の「items.db」の内容ならitem_listは以下になります。


[{'id': 1, 'name': 'yamada', 'age': 30}, {'id': 2, 'name': 'sato', 'age': 22},
 {'id': 3, 'name': 'kawano', 'age': 33}]

実行結果
「http://localhost:8080/list」でデータリストページが表示されました。
データリストページ

新規登録 /add

「http://localhost:8080/add」でデータを追加できるようにします。
Name、Ageの入力にテンプレートにadd_tmp.tplを使っています。
データ登録ページ


テンプレート  add_tmp3.tpl
<!DOCTYPE html>
<html>
<h3>input item name</h3>
<form method='POST'>
<input type='text' name='item_name' placeholder='Name'/>
<input type='text' name='item_age' placeholder='Age'/>
<input type='submit' value='登録'/>
</form>
</html>

スクリプト   web_lst_01b.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from bottle import route, run, template, request, redirect
import sqlite3
# /add 新規登録
@route('/add',method=['GET','POST'])
def add_item():
    if request.method=='POST':
        item_name=request.POST.getunicode('item_name')
        item_age=request.POST.getunicode('item_age')        
        con=sqlite3.connect('items.db')
        cur=con.cursor()

        #max ID
        c=cur.execute('SELECT max(id) FROM items')
        max_id=c.fetchone()[0]
        new_id=max_id+1
        #new_id=cur.execute('SELECT max(id)+1 FROM items').fetchone()[0]
        # table が空の場合にid=1を設定する
        if new_id==None:
            new_id=1
             
        cur.execute('INSERT INTO items VALUES(?,?,?)',(new_id,item_name,item_age))
        con.commit()
        con.close
        
        return f'SUCCESS id={new_id}, name={item_name}, age={item_age}'        
        #redirect list-page 
        #return redirect('/list')        
    else:
        #add-page
        return template('add_tmp3')

run(host='localhost', port=8080, debug=True)

部分説明
「http://localhost:8080/add」でelse文の新規登録のページになります。

新規登録のページで名前、年齢を入力、登録ボタンを選択するとmethod==’POST’となり、値がデータベースに追加されます。
SQL文”SELECT max(id) FROM items”でmax(id)の値が得られます。
.fetchone()[0]でmax_id値を得ます。新規登録のnew_idはmax_id+1になります。

‘INSERT INTO items VALUES・・データベースに登録後、return f’SUCCESS id={new_id},・・登録した名前、年齢を表示します。
実使用ではデータ表示ページに戻すので、return redirect(‘/list’)にします。

実行結果
名前をkato、年齢を28で登録した場合です。
データ登録後の表示
データ表示ページで確認すると登録が確認できます。今回はここまでになります。
データリストページで新規データを確認

まとめ

bottlとsqlite3を使って名前と年齢のデータベースからデータの一覧表示ページと新規登録ページを作成できました。次回はデータの削除、変更ページを作成します。