Pylonsでメモアプリ(1)
実際にアプリを作ってみて勉強してみます。
っていうかWebアプリを実装してみたいだけです。
仕様は一般的な感じで、
- 作成、一覧、変更、削除
- タグつけて管理
- 一覧はページャつける
- 検索
- ユーザー認証
- ひとまずAjaxはしない
とこんな感じを目標に、試行錯誤しながら作ってみます。
いざ作るにあたり、特に下の2つのブログでとても勉強させていただきました。
スコトプリゴニエフスク通信
aodag blog
RESTコントローラに関する記事とか、本当に勉強になりました。
1つめのd:id:perezvonさんのPylonsチュートリアルはめちゃめちゃ分かりやすいです。
モデルを作成する
DBにはMySQL、ORMにはSQLAlchemyを使いました。
pasterでモデルからテーブルを作成することができますが、
データベースはあらかじめ自分で作っておく必要があります。
モデルの内容は /model/__init__.pyに書く。
メモとタグの関係は多対多になるので、2つを関連付けるテーブルを作って管理する。
というわけで、メモ・タグ・メモとタグの関連付けと3つのテーブルを作りました。
そのうち、モデルとして操作するのはメモとタグなので、クラスとテーブルをマッピングします。
# coding=utf-8 from sqlalchemy import * from sqlalchemy.ext.assignmapper import assign_mapper from sqlalchemy.orm import relation from datetime import datetime from sacontext import PylonsSAContext sac = PylonsSAContext() sac.add_engine_from_config(None) # definition table notes = Table('note', sac.metadata, Column('id', Integer, primary_key=True), Column('title', Unicode(250), nullable=False), Column('content', Unicode), Column('posted_at', DateTime, default=datetime.now()), Column('modified_at', DateTime, default=datetime.now()), Column('user_id', Integer, ForeignKey("user.id"), nullable=False), Column('task_id', Integer, ForeignKey("task.id"), default=''), ) notetags = Table('notetag', sac.metadata, Column('id', Integer, primary_key=True), Column('name', Unicode(30), unique=True, nullable=False), ) note_tag_relation = Table('note_notetag', sac.metadata, Column('note_id', Integer, ForeignKey("note.id")), Column('notetag_id', Integer, ForeignKey("notetag.id")), ) users = Table('user', sac.metadata, Column('id', Integer, primary_key=True), Column('username', String(255), unique=True, nullable=False), Column('password', String(255), nullable=False), Column('email', String(255), unique=True, nullable=False), Column('created_at', DateTime, default=datetime.now()), Column('lastlogin', DateTime), ) # Domain Object class Note(object): def __init__(self, title=None, content=None): self.title = title self.content = content class NoteTag(object): def __init__(self, name=None): self.name = name class User(object): def __init__(self, username=None, password=None, email=None): self.username = username self.password = password self.email = email # mapping assign_mapper(sac.session_context, NoteTag, notetags) assign_mapper(sac.session_context, Note, notes, properties= { 'tags': relation(NoteTag, secondary=note_tag_relation, lazy=False) } ) assign_mapper(sac.session_context, User, users)
sac = PylonsSAContext でSAContextを作成して利用します。
あとはSQLAlchemyの流儀にしたがって、クラスとテーブルをマッピングするだけ。
テーブルとクラスが一体化してないのがSQLObjectとの大きな違いだなー。
日本語が入る可能性があるカラムは、Unicode()を指定します。引数を指定しない場合は、textタイプになるみたいです。
development.iniにDBの設定を書く
development.iniにSQLAlchemyを使うよという指定と、使うデータベースの指定をします。
sqlalchemy.default.uri = mysql://cheesepie@localhost/testapp?charset=utf8 sqlalchemy.default.echo = true sqlalchemy.default.echo_pool = false sqlalchemy.default.pool_recycle = 3600
テーブル定義でUnicode()をカラムに指定している場合は、文字通りユニコードの
データとして扱われるので、development.iniでの指定だけじゃなくて、
MySQL側の文字コードも my.cnf 内でUTF-8の指定を記述しておく必要があります。
(これでハマりました・・)
テーブルを作成する
DBテーブルは定義したモデルと設定ファイルに従って、pasterを使って自動で作成できます。
paster setup-app development.ini
このとき、テストデータも登録できるので入れてしまった方が便利です。
テストデータはwebsetup.pyに記述します。
from sqlalchemy import exceptions from testapp.model import * try: first_user = User('foo', 'foo', 'foo@foo.com') first_user.id = 2 print "Add user..." for i in range(30): note = Note('Foo\'s Test'+str(i), u'テスト投稿') note.user_id = first_user.id nt = sac.session_context.current.query(NoteTag).get_by(name='foo') if nt is None: note.tags.append(NoteTag('foo')) else: note.tags.append(nt) nt = sac.session_context.current.query(NoteTag).get_by(name='sample') if nt is None: note.tags.append(NoteTag('sample')) else: note.tags.append(nt) sac.session_context.current.save(note) print "Add note..." sac.session_context.current.flush() except exceptions.SQLError, e: pass
いやーいちいち調べながらなので遅い遅い。。。
でもドキュメント見まくってたらSQLAlchemyの仕様がだいぶ分かってきた気が
します、あくまで気が。
テーブルの数が増えたりwhereの条件が増えたりしても耐えられる仕様になっている
のがいいと思いました。
次回からはコントローラの作成をば!