Pyramid を試してみる
注意:このポストを書いた当時は出て数日で 1.0 どころではなかったので古い内容となってます。
- Good night, Posterous
- http://d.hatena.ne.jp/imagawa_yakata/
- podhmoの日記
- Pyramid チュートリアル — Pyramid Tutorial for PyCon JP Sprint 1.0 documentation
などを参考にする事をお勧めします。
今の仕事で Flask でプロトタイプ(社内限定で公開)を書いてたけど、外にも公開したいと言われた。
作ったものはとりあえず動くレベルで外に公開するには諸々足らない。
Flask もエクステンションを組み合わせて作り上げれば出来るのは分かってるけど、時間がないので楽をしようと他の WAF にする事にした。
Django でも良かったんだけど、前から気になってたのと、SQLAlchemy, Jinja2 をほぼそのまま流用できるという理由で Pylons にした。
で、Github や Bitbucket で良い Pylons のサンプルが無いかと探していた時に、Pyramid というものを見つけた。
# ちょうどプロジェクトのページが出来て 2, 3 日しかたってなかった模様
やたらドキュメントが充実してるなと印象を持ってドキュメントをビルドしてみたら、repoze.bfg をリネームしただけ?と思ってた。
# 恥ずかしながら repoze.bfg はその時初めて知った。
で、昨日 @aodag さんが pyramid について色々ツイートされてて、それ経由で色々知った。
詳しくは Good night, Posterous 参照。
また、
インストール&プロジェクト作成
チュートリアルをそのままやるのが普通なんだろうけど、折角なので簡単なアプリケーションを作ってみた。
$ pip install pyramid $ pip install zope.sqlalchemy $ pip install repoze.tm2 $ paster create -t pyramid_routesalchemy microblog
SQLAlchemy を使ったサンプルとして作る。
paster のテンプレートには他にも色々ある。
routesalchemy と alchmey の違いは何なんだろう。
あと、Pylons みたいなクラスベースの view を作る事が出来るみたい。
# こっちはまた今度試す
設定ファイル
[DEFAULT] debug = true [app:sqlalchemy] use = egg:microblog#app reload_templates = true debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = ja db_string = sqlite:///%(here)s/microblog.db db_echo = false jinja2.directories = microblog:templates [pipeline:main] pipeline = egg:repoze.tm2#tm sqlalchemy [server:main] use = egg:Paste#http host = 0.0.0.0 port = 6543
モデルを作る
models.py
# -*- coding: utf-8 -*- import datetime from sqlalchemy import create_engine, Column, Integer, Unicode, Text, DateTime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import scoped_session, sessionmaker from zope.sqlalchemy import ZopeTransactionExtension DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) Base = declarative_base() class Microblog(Base): __tablename__ = 'microblog' id = Column(Integer, primary_key=True) title = Column(Unicode(255)) text = Column(Text()) created_at = Column(DateTime, default=datetime.datetime.now()) def __init__(self, title, text): self.title = title self.text = text def __repr__(self): return "<Microblog('%s', '%s', '%s', '%s')>" % (self.id, self.title, self.text, self.created_at) def initialize_sql(db_string, db_echo=False): engine = create_engine(db_string, echo=db_echo) DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine)
sqlite3 で microblog というテーブルがあるという想定。
ルーティングの設定
__init__.py
# -*- coding: utf-8 -*- from pyramid.configuration import Configurator from paste.deploy.converters import asbool from pyramid_jinja2 import renderer_factory from microblog.models import initialize_sql def app(global_config, **settings): """ This function returns a WSGI application. It is usually called by the PasteDeploy framework during ``paster serve``. """ db_string = settings.get('db_string') if db_string is None: raise ValueError("No 'db_string' value in application configuration.") db_echo = settings.get('db_echo', 'false') initialize_sql(db_string, asbool(db_echo)) config = Configurator(settings=settings) config.begin() config.add_renderer('.jinja2', renderer_factory) config.add_static_view('static', 'microblog:static') config.add_route('home', '/', view='microblog.views.index') config.add_route('add', '/add', view='microblog.views.add', view_renderer='entries.jinja2') config.end() return config.make_wsgi_app()
Pylons でいうところの、environment.py にあたるのかな?
views を作る
views.py
# -*- coding: utf-8 -*- import transaction from pyramid.view import view_config from pyramid.renderers import render from pyramid.response import Response from microblog.models import DBSession, Microblog @view_config(renderer='.jinja2') def index(request): dbsession = DBSession() entries = dbsession.query(Microblog).order_by(Microblog.id.desc()).all() result = render('entries.jinja2', {'entries': entries}) DBSession.remove() return Response(result) def add(request): requests = request.params title = requests['title'] text = requests['text'] dbsession = DBSession() model = Microblog(title=title, text=text) dbsession.add(model) dbsession.flush() transaction.commit() entries = dbsession.query(Microblog).order_by(Microblog.id.desc()).all() result = render('entries.jinja2', {'entries': entries}) DBSession.remove() return Response(result)
/index でデータベースの内容を全て表示、/add で Post の内容を追加する。
/add の方にデコレータがないのは、__init__.py の方で設定できるみたい。
テンプレート
Flask のテンプレートを拝借。
layout.jinja2
<!doctype html> <title>The Pyramid Web Application Development Framework</title> <link rel="shortcut icon" href="{{request.application_url}}/static/favicon.ico" /> <link rel="stylesheet" href="{{request.application_url}}/static/style.css" type="text/css" media="screen" charset="utf-8" /> <body> <div class="page"> <h1>Pyramid</h1> {% block entries %}{% endblock %} </div> </body> </html>
entries.jinja2
{% extends "layout.jinja2" %} {% block entries %} <form action="/add" method="post"> <dl> <dt>Title:</dt> <dd><input type="text" size="30" name="title"></dd> <dt>Text:</dt> <dd><textarea name="text" rows="5" cols="40"></textarea></dd> <dd><input type="submit" value="Share"></dd> </dl> </form> <ul class="entries"> {% for entry in entries %} <li><h2>{{ entry.title }}</h2>{{ entry.text }} {% else %} <li><em>Unbelievable. No entries here so far</em> {% endfor %} </ul> {% endblock %}
色々作る過程ではまったけど、簡単なのは作れた。
1.0αだし、オフィシャルのドキュメント以外の情報がないので、仕事で使うにはまだ早いかな…。
# ドキュメントは充実している
Pylons 自体はメンテナンスモードに移行との事なので、新規に開発される事はなさそう。
これから色んな人が使って行って成熟していくまでは、Pylons を使うとしよう。
# マルチデータベースとか、テスト用のデータベース作成とかそこら辺
# 自分でゴリゴリ変更できるんだろうけど、そういうのはフレームワーク側でよろしくやって欲しい