読者です 読者をやめる 読者になる 読者になる

Bottleとpeeweeを使ってゲストブックアプリケーションを作ってみた

Python用のWebフレームワークであるBottleと、O/Rマッパーのpeeweeを使って、Python Professional Programmingの2章のゲストブックアプリケーションを作ってみた。
試したバージョンは、Python2.7、Python3.3、Bottle0.11.6、peewee2.1.5。

ソースコード

guestbook.py
# coding: utf-8
import os
from datetime import datetime

import peewee
from bottle import route, get, post, request, run
from bottle import template, static_file, redirect, html_escape

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_FILE = os.path.join(BASE_DIR, 'guestbook.dat')
STATIC_DIR = os.path.join(BASE_DIR, 'static')
db = peewee.SqliteDatabase(DATA_FILE)


class Greeting(peewee.Model):
    """投稿データのモデル
    """
    name = peewee.CharField()
    comment = peewee.TextField()
    create_at = peewee.DateTimeField()

    class Meta:
        database = db


def create_table():
    """データベースファイルがなければデータベーステーブルを作成します
    """
    if not os.path.exists(DATA_FILE):
        Greeting.create_table()


def save_data(name, comment, create_at):
    """投稿データを保存します
    """
    Greeting.create(name=name, comment=comment, create_at=create_at)


def load_data():
    """投稿されたデータを返します
    """
    greeting_list = Greeting.select().order_by(Greeting.create_at.desc())
    return greeting_list


@get('/')
def index():
    """トップページ
    テンプレートを使用してページを表示します
    """
    greeting_list = load_data()
    return template('index', greeting_list=greeting_list)


@post('/post')
def post():
    """投稿用URL
    """
    name = request.forms.name
    comment = request.forms.comment
    create_at = datetime.now()
    # データを保存します
    save_data(name, comment, create_at)
    return redirect('/')


@route('/static/<filename:path>')
def send_static(filename):
    """静的ファイルを返します
    """
    return static_file(filename, root=STATIC_DIR)


def nl2br(s):
    """改行文字をbrタグに置き換える関数
    """
    return html_escape(s).replace('\n', '<br />')


def datetime_fmt(dt):
    """datetimeオブジェクトを見やすい表示にする関数
    """
    return dt.strftime('%Y/%m/%d %H:%M:%S')


if __name__ == '__main__':
    create_table()
    run(host='127.0.0.1', port=5000)
index.tpl

Bottleのテンプレート内ではPythonコードを書ける。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <title>ゲストブック</title>
    <link rel="stylesheet" href="/static/main.css" type="text/css" />
  </head>
  <body>
    <div id="main">
      <h1>ゲストブック</h1>
      <div id="form-area">
        <p>書き込みをどうぞ。</p>
        <form action="/post" method="post">
          <table>
            <tr>
              <th>名前</th>
              <td>
                <input type="text" size="20" name="name" />
              </td>
            </tr>
            <tr>
              <th>コメント</th>
              <td>
                <textarea rows="5" cols="40" name="comment"></textarea>
              </td>
            </tr>
          </table>
          <p><button type="submit">送信</button></p>
        </form>
      </div>
      <div id="entries-area">
        <h2>これまでの書き込み</h2>
        % from guestbook import nl2br, datetime_fmt
        % for greeting in greeting_list:
        <div class="entry">
          <h3>{{ greeting.name }} さんの書き込み({{ datetime_fmt(greeting.create_at) }}):</h3>
          <p>{{! nl2br(greeting.comment) }}</p>
        </div>
        % end
      </div>
    </div>
  </body>
</html>
static/main.css
body {
  margin: 0;
  padding: 0;
  color: #000E41;
  background-color: #004080;
}
h1 {
  padding: 0 1em;
  color: #FFFFFF
}
#main {
  padding: 0;
}
#form-area {
  padding: 0.5em 2em;
  background-color: #78B8F8;
}
#entries-area {
  padding: 0.5em 2em;
  background-color: #FFFFFF;
}
.entry p {
  padding: 0.5em 1em;
  background-color:  #DBDBFF;
}

ソースコード一式はbitbucketにも置いときました。
tokibito / python-professional-programming / source / 02-guestbook-bottle-peewee — Bitbucket

実行結果

Python2.7とPython3.3のどちらでも動きます。

雑感

BottleはFlaskと似たようなAPIになっていますね。ただ機能面ではWerkzeug、Jinja2の組み合わせのFlaskのほうが使い勝手がいいかも。
peeweeはシンプルにモデルが書けて使いやすいですね。使い込んでないので機能面でいいのかはわからないけども。