いろんなもの使ってSBM その2 データアクセス
さて、ブックマークを保存する部分を考えよう。
深く考えずに、ひとまずDBに入れることにする。
データアクセスにはSQLAlchemyを使う。
オブジェクトをテーブルをまたいでマッピングできたり、継承構造をマッピングする方法を選択できたりと柔軟なO/Rマッパーだ。
DB情報保持のためのモジュールを先に作っておこう。
# database.py from sqlalchemy import * from sqlalchemy.ext.sessioncontext import SessionContext meta = DynamicMetaData() context = SessionContext(create_session)
metaはDBに関する情報を持っている。
contextはスレッドローカルなセッションを保持していて、実際のセッションにはcontex.current でアクセスできる。
どちらもグローバルに使用できるように作られていて、プログラム中では、直接この2つを扱えばいい。
ここで、モデルを考えよう。
主なモデルは2つブックマークとユーザーだ。
ユーザーはブックマークを複数持っている。
ここで、ブックマークはユーザーが自由に入力したものとして扱い、複数ユーザーで共有することはないものとする。
つまり1:N関連となるわけだ。
あとは最近のソーシャルブックマークから適当に引っ張ってこよう。
タグ付けは必須機能と言っていいほど、どのSBMも持っている機能だ。
また、ブックマークにメモ書きできる。
ラベルはユーザー共通としておこう。
u = User() b = Bookmark() l = Label() u.name u.bookmarks u.labels u.password b.title b.url b.memo b.user b.labels l.name l.bookmarks
ラベルの識別子はラベル名(name)、bookmarkはユーザーごとにurlが識別子、ユーザーはユーザー名が識別子となる。
label = Label.byName(labelname) user = User.byName(username) bookmark = user.bookmarkByUrl(url) bookmarks = user.selectBookmarkByLabel(label)
Label.byNameでは該当するラベルがなかった場合は、その名前で作成したラベルを作ってしまえばいい。
user.bookmarkByUrlも同様に考えることにしよう
としても、既存のものかどうかは知りたいと思うので、それを調べるメソッドも考えよう。
Label.exists(labelname) User.exists(username) user.hasBookmark(url)
さてこいつらをテーブルにマッピングしよう。
基盤として、クラスをそのままテーブルに割り当ててみる
from database import meta user_table = Table("User", meta) bookmark_table = Table("Bookmark", meta) label_table = Table("Label", meta)
それぞれに機械的なIDをつける
user_table = Table("User", meta, Column("user_id", Integer, primary_key=True)) bookmark_table = Table("Bookmark", meta, Column("bookmark_id", Integer, primary_key=True)) label_table = Table("Label", meta) Column("label_id", Integer, primary_key=True))
関連付けのために外部キーを付ける
user_table = Table("User", meta, Column("user_id", Integer, primary_key=True)) bookmark_table = Table("Bookmark", meta, Column("bookmark_id", Integer, primary_key=True), Column("user_id", Integer, ForeignKey("user.user_id"))) label_table = Table("Label", meta, Column("label_id", Integer, primary_key=True))
N:M関連のためにブリッジテーブルを追加する
label_bookmark_table = Table("label_bookmark", meta, Column("label_id", Integer, ForeignKey("label.label_id")), Column("bookmark_id", Integer, ForeignKey("bookmark.bookmark_id")))
識別子の列を追加する
user_table = Table("User", meta, Column("user_id", Integer, primary_key=True), Column("name", String, unique=True)) bookmark_table = Table("Bookmark", meta, Column("bookmark_id", Integer, primary_key=True), Column("user_id", Integer, ForeignKey("user.user_id")), Column("url", String)) bookmark_table.append_constraint(UniqueConstraints("user_id", "url")) label_table = Table("Label", meta, Column("label_id", Integer, primary_key=True), Column("name", Unicode, unique=True))
追加の情報について列を追加
bookmark_table = Table("Bookmark", meta, Column("bookmark_id", Integer, primary_key=True), Column("user_id", Integer, ForeignKey("user.user_id")), Column("url", String), Column("title", Unicode), Column("memo", Unicode)) bookmark_table.append_constraint(UniqueConstraints("user_id", "url"))
マッピング
assign_mapperを使って少し楽に使えるようにしておこう。
#database.py from sqlalchemy import * from sqlalchemy.ext.sessioncontext import SessionContext from sqlalchemy.ext.assignmapper import assign_mapper meta = DynamicMetaData() context = SessionContext(create_session) def assgin(*args, **kwargs): return assign_mapper(context, *args, **kwargs)
とりあえずそれぞれのクラス定義をしておく
class Bookmark(object): pass class Label(object): pass class User(object): pass
クラスとテーブルをマッピング
assgin(User, user_table) assign(Bookmark, bookmark_table) assign(label, label_table)
関連をマッピング
assign(Bookmark, bookmark_table, properties=dict(user=relation(User, backref="bookmarks"), labels=relation(Label, secondary=label_bookmark_table, backref="bookmarks"))