Python 自習室に参加してきた

3 月に一回参加しているけど、Python 自習室という名前になってからは初めての参加。
場所はアクセンス・テクノロジー様の大阪オフィス。いつもありがとうございます<(_ _)>


前回は Paver でオレオレツールを作って、今回は何しようかなーと迷ってたが、Flask を試してみようかと思っていじった。
Welcome | Flask (A Python Microframework)


サンプルを実行してみたら、あっさり動いた。
それだけじゃあれなので、SQLAlchemy と連携してみた。
Flask-SQLAlchemy というのがあるのは知っているが、個人的にスキーマ定義は sql ファイルでやりたい。
SQLAlchemy にテーブル定義を書くのではなく、定義を自動的にロードするのはこちらを参考にした。
今日も明日もググったー: autoload declarative sqlalchemy


構成はこんな感じ。

|-- app.cfg
|-- app.py
|-- model
|   |-- __init__.py
|   |-- base.py
|   `-- entries.py
|-- schema.sql
|-- static
|   `-- style.css
`-- templates
    |-- layout.html
    |-- login.html
    `-- show_entries.html

app.cfg

SQLALCHEMY_ECHO = False
DEBUG = True

SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'

schema.sql

drop table if exists entries;
create table entries (
  id    integer primary key autoincrement,
  title text not null,
  text  text not null
);

app.py

from flask import Flask, request, session, redirect, url_for, abort, \
     render_template, flash

from model.base import db_session
from model.entries import Entry

app = Flask(__name__)
app.config.from_pyfile('app.cfg')

@app.route('/')
def show_entries():
    entries = db_session.query(Entry).all()
    return render_template('show_entries.html', entries=entries)

@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != app.config['USERNAME']:
            error = 'Invalid username'
        elif request.form['password'] != app.config['PASSWORD']:
            error = 'Invalid password'
        else:
            session['logged_in'] = True
            flash('You were logged in')
            return redirect(url_for('show_entries'))
    return render_template('login.html', error=error)
@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    flash('You were logged out')
    return redirect(url_for('show_entries'))

@app.route('/add', methods=['POST'])
def add_entry():
    if not session.get('logged_in'):
        abort(401)

    entry = Entry(title=request.form['title'], text=request.form['text'])
    db_session.add(entry)
    db_session.commit()

    flash('New entry was successfully posted')
    return redirect(url_for('show_entries'))

if __name__ == '__main__':
    app.run()

model/base.py

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
uri = 'sqlite:////tmp/flaskr.db'
engine = create_engine(uri, convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))

Base = declarative_base(bind=engine)
Base.query = db_session.query_property()

model/entries.py

from base import Base

class Entry(Base):
    __tablename__ = 'entries'
    __table_args__ = { 'autoload': True }

    def __init__(self, title=None, text=None):
        self.title = title        self.text = text

    def __repr__(self):
        return '<Entry(%s, %s)>' % (self.title, self.text)

こういう普段試したいと思いつつ中々実行に移せないのをじっくり時間をとって、試す事ができるのは素晴らしいと思う。