Bye Bye Moore

PoCソルジャーな零細事業主が作業メモを残すブログ

bottleとsqlite3を連携させて簡単なメモ表示ツール その3:テンプレートエンジンJadeを使う

bottleにはデフォでerbっぽいテンプレートエンジンが搭載されています。
……とはいえ、今更タグ閉じなんてカッたるい事をやる気にもなりません。
ここは一つ先人の叡智をお借りして、jadeをbottleで使ってしまいましょう

実際のところ

導入

まずは導入。
jadeの環境がある状態で、以下のコマンドを実行。
最終更新が2年前というのが若干きになりますが……。

$ pip install bottle-jade

python部分

冒頭部分

諸々のライブラリまわり。
今までのモノに加えて、Bottle本体とbottle_jadeを呼び出し。
URLの指定を楽にするため標準搭載のosライブラリも。

from bottle import get, post, request, run, redirect, Bottle
from time import strftime
import sqlite3

from bottle_jade import JadePlugin
from os import path as op

conn = sqlite3.connect('blogtest.sqlite')

app = Bottle()
templates = op.dirname(op.abspath(__file__)) + '/templates/'
jade = app.install(JadePlugin(template_folder=templates))
本体

で、本体。
GetとPostのときに弄ったBlogまわり

BASE_QUERY_FOR_BODY = 'select body from blog '

@get('/blog')
def recentBlogPost():
    query = BASE_QUERY_FOR_BODY + 'order by rowid desc limit 1;'
    cur = conn.cursor()
    for row in cur.execute(query):
      body = row
    context = {"body": body} 
    return jade.render('blog.jade', **context)

@get('/blog/<num>')
def someBlogPost(num):
    query = BASE_QUERY_FOR_BODY + 'where rowid=' + str(num)
    cur = conn.cursor()
    for row in cur.execute(query):
      body = row
    context = {"body": body} 
    return jade.render('blog.jade', **context)      

@post('/blog')
@post('/blog/<num>')
def postBlog():
    body = request.forms.get('body')
    query = 'INSERT INTO blog VALUES ("' + body +'",date("now"))'
    conn.execute(query)
    return redirect('/blog')

jade部

blog.jade
h1 blog posts
p #{body[0]}
hr

form(action="/blog" method="post")
  b please input blog contents. >>
  input(name="body" type="text")
  input(value="Post" type="submit")

見え方

f:id:shuzo_kino:20160919214355p:plain

bottleとsqlite3を連携させて簡単なメモ表示ツール その2:POSTする

前回の続きで、今回はPOST動作です。

実際のところ

まず、子パッケージのpostとrequestを突っ込みます。

from bottle import get, post, request, run, template, static_file, error

getの後にpostを追記。

@post('/blog')
def postBlog():
    body = request.forms.get('body')
    query = 'INSERT INTO blog VALUES ("' + body +'",date("now"))'
    conn.execute(query)
    return '<p>posted!</p>'

これに合わせた投稿処理も

@get('/blog')
def recentBlogPost():
    # 略
    postform = '<form action="/blog" method="post"><input name="body" type="text" /><input value="Post" type="submit" /></form>'
    return template('<p>{{body}}</p></hr>' + postform, body=body[0])

bottleとsqlite3を連携させて簡単なメモ表示ツール その1:存在するデータの表示

bottleとsqlite3を連携させて簡単なメモ表示ツールをつくる方法です。
第一回目は存在するDBからデータの表示する方法です。

実際のところ

あらかじめ、blogpost.sqliteというDBを作った上でtableも用意しておきます。

  • /blogにアクセスしたら、最新の記事
  • /blog/にアクセスしたらnum番目の記事

という表示を考える場合、以下のような感じになります。

from bottle import get, run, template
import sqlite3

conn = sqlite3.connect('blogtest.sqlite')

BASE_QUERY_FOR_BODY = 'select body from blog '

@get('/blog')
def recentBlogPost(num):
    query = BASE_QUERY_FOR_BODY + 'order by rowid desc limit 1;'
    cur = conn.cursor()
    for row in cur.execute(query):
      body = row
    return template('<p>{{body}}</p>!', body=body[0])

@get('/blog/<num>')
def recentBlogPost(num):
    query = BASE_QUERY_FOR_BODY + 'where rowid=' + str(num)
    cur = conn.cursor()
    for row in cur.execute(query):
      body = row
    return template('<p>{{body}}</p>', body=body[0])

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

bottleのredirectとabort

処理エラーやURLミスなどのときに活用したい一連の処理。
最小構成のbottleはこういうモノも外付けパッケージです。
種類としては、特定のURLに飛ばすredirectと自動生成タイプのabortがあります。

実際のところ

abort

特定のエラーコード付きでエラーページを出したい場合はabortを使います

from bottle import route, abort
@route('/restricted')
def restricted():
    abort(401, "Sorry, access denied.")

Redirect

一方、レスポンスコードは兎も角、
特定のURLに直に飛ばしたい場合はredirectを使います。
古いページを隠蔽したい場合なんかでも使い道がありますね。

from bottle import redirect
@route('/wrong/url')
def wrong():
    redirect("/right/url")

優先処理されるので注意!

以下の記述によると自前で用意したコールバックより処理が優先されるようなので注意してください。

Both functions will interrupt your callback code by raising an HTTPError exception.

SQLiteでFIRSTやLASTみたいな事を実現する

SQLiteには、FIRSTやLASTに相当する構文はありません。
内部的に印加されるrowidというデータをソートして、LIMITで一個だけ採ってきます。

select rowid,body,date from blog order by rowid desc limit 1;
/* 5|it is a fine today.|2016-09-15 */

実際のところ

こんなDBがあったとします。

select * from blog;
/*
my first blog post.|2016-09-14
my second blog post.|2016-09-16
my first blog post.|2016-09-16
it is a fine today.|2016-09-15
*/

これについて、デフォで設定される通し番号rowidを印加すると、こんな感じ。

select rowid, body, date from blog;
/*
1|my first blog post.|2016-09-14
3|my second blog post.|2016-09-16
4|my first blog post.|2016-09-16
5|it is a fine today.|2016-09-15
*/

これを活かして、FIRSTならasc、LASTならdescで並べ替え、LIMIT 1で取り出します。

select rowid,body,date from blog order by rowid desc limit 1;
/* 5|it is a fine today.|2016-09-15 */
select rowid,body,date from blog order by rowid asc limit 1;
/* 1|my first blog post.|2016-09-14 */

SQLiteの時間型……はないが関数はある。

SQLiteには、明確な時間型というのがありません。
かわりに、文字列やユリウス暦UNIX時間を数値化できる各種関数をもってサポートしています。

SQLite does not have a storage class set aside for storing dates and/or times. Instead, the built-in Date And Time Functions of SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values:

実際のところ

今の日付・時間

時間はtime関数、日付はdate関数、全部ほしければdatetime関数です

SELECT time('now');
/* 12:04:12 */

SELECT date('now');
/* 2016-09-14 */

SELECT datetime('now');
/* 2016-09-14 12:06:03 */

UNIX時間で欲しい場合はstrftime関数です。
記法はよくあるprintf系

SELECT strftime('%s', "2016-09-08");

テーブルの作成

上記の通り、決まったデータ型はないので遣りやすい方法でやればいいでしょう。
私は楽したいのでTEXT型。

CREATE TABLE blog(body TEXT, date TEXT);

データの挿入

現在の日付を入れたいだけなら、date('now')で入る親切設計。
……INTEGER形式で入れる場合はstrftime('%s', 'now')でコンバートしてから

INSERT INTO blog VALUES ("my first blog post.", date('now'));

日付の指定も出来ます。
……間違って「最初の投稿」が二個になっちゃいましたね。
これは後でフォローします。

INSERT INTO blog VALUES ("my first blog post.", date("2016-09-16"));
INSERT INTO blog VALUES ("my second blog post.", date("2016-09-16"));

検索

検索するにはWHERE節で。
私は楽したいので文字列でやってます。
高速にやりたい人はUNIX時間とかでも良いでしょう。

SELECT body FROM blog WHERE date(date) = '2016-09-16';
/*
my first blog post.
my second blog post.
*/

削除

先ほど二個作ってしまった"最初の投稿"を消しましょう

DELETE FROM blog WHERE date(date) = '2016-09-16' AND body = 'my first blog post.';

SELECT * FROM blog;
/*
my first blog post.|2016-09-14
my second blog post.|2016-09-16
*/

SQLite3の全文検索機能"FTS"をつかってみる その5:一致箇所の強調

SQLite FTSの不思議な機能の一つにThe Snippet Functionというのがあります。
一致した箇所を強調してくれる奴です。

実際のところ

デフォではbタグで囲ってくれます。
HTMLやXMLなんかの時には便利?

SELECT snippet(eb2) FROM eb2 WHERE body MATCH 'all';
/* <b>All</b> your base are belong to us. */

囲い文字は変更できます。
WIkiアプリ作る時便利かも……

 SELECT snippet(eb2, '{', '}') FROM eb2 WHERE body MATCH 'all';
/* {All} your base are belong to us. */