なかまはずれクイズ(プレゼントもするでよ)

やっほー。よいこのみんなー。おじさん あつさであたまがやられたので、クイズをだしてみるよー。

なかまはずれはどれかな?

したの中で、なかまはずれのこは何番かなー?

1. もんがーなやーつ

@hoge = [];
$hoge[2] = "two";
$hoge[0] = "zero";
$hoge[1] = "one";

foreach $h (@hoge) {
    print $h , "\n";
}

2. いすとなやーつ

hoge = []
hoge[2] = "two"
hoge[0] = "zero"
hoge[1] = "one"

for h in hoge
    puts h
end

3. にすたなやーつ

hoge = [''] * 3
hoge[2] = "two"
hoge[0] = "zero"
hoge[1] = "one"

for h in hoge:
    print h

4. ちぱーなやーつ

<?php

$hoge = array();
$hoge[2] = "two";
$hoge[0] = "zero";
$hoge[1] = "one";

foreach($hoge as $h) {
    print $h . "\n";
}

プレゼント応募方法

  • コメント欄に、解答の番号と、AmazonのウィッシュリストのURLを一緒に投稿してください。
  • 解答内容に関しては記載しないでください。

当選発表

  • 当選者の発表は賞品の発送をもって代えさせていただきます
  • 気が向いたら、正解者の中から、適当に1名選んで、ウィッシュリストの中のアイテムを送付します。
  • 多分面識のある人と、安くておもしろそうなアイテムをウィッシュリストに突っ込んでる人が当選確度が高いです。
  • 解答は面倒なんで特に発表しません。
  • 質問は一切受け付けません。

みんなふるって応募してねー(ゝω・)vキャピ

Python の Coding Style あれこれ

唐突ですが、Python の Coding Style について参考になりそうなものをまとめてみるテスト。 主にベースとなるPEP8の他に、僕が見聞きしたWebフレームワークのCoding Styleを列挙しています。

PEP 8 Style Guide for Python Code

PEP 8 は Pythonを書く上で大体みんなが参考にするCoding Styleです。以下にあげる Coding Style達は、PEP8 に従い、その中で独自のCoding Styleを定義しているものがほとんどです。

PEP が初耳という人は、下記エントリーが参考になるでしょう。

PEP 257 Docstring Conventions

こちらは docstring の 表記に関する PEP です。Pythonでは docstringを利用してモジュールのヘルプや、関数・クラスへのコメントを書く事が推奨されています。そのdocstringをどう書くべきかについて、書かれています。

Google Python Style Guide

Googleの出しているガイドです。pycheckerの話、引数のdefault値の話など、割と広範に及んでいます。メリット、デメリットが明確に記載されているので、なぜそれが必要なのか分かりやすくなっています。いま気づいたのは、おまけ的にvimrcの設定もついてるんですね。

The Pylons Project | Coding Style and Standards

Pyramid (http://www.pylonsproject.org/projects/pyramid/about) というフレームワークで有名な、Pylonsプロジェクト(http://www.pylonsproject.org/)が出しているCoding Style です。Styleそのものは PEP8 に準拠していて、その他 Pylonsプロジェクトに貢献するための作法についても書かれています。

Styleとは別の話ですが、ユニットテストについてのガイドラインも参考になると思いました。

Django | Coding style

言わずと知れた Webフレームワーク Django の Coding Styleです。こちらもPEP8を基本としつつ、model, view, template等に関するstyleがか書かれています。日本語訳はちょっと見つかりませんでした。

The Pocoo Style Guide

Flask(http://flask.pocoo.org/) や Werkzeug(http://werkzeug.pocoo.org/) 、 Sphinx(http://sphinx.pocoo.org/)といったフレームワークやツールを作っている、Pocooという集団?の提案するCodingStyleです。やはりPEP8がベースとなっています。個人的には moduleのヘッダー部分のコメントの書き方は参考になりました。

Zope | Coding Style

僕はあまり知らないのですが、Zope は古くからある フレームワークで、CMSPlone (http://plone.org/) や、Ubuntuの開発等でも利用されるBTSのLaunchPad(https://launchpad.net/) で使われている事でも有名らしいです。こちらもPEP8に準拠しています。個人的には「# XXX」 や「# BBB」といったコメントの書き方は、初めて知りました。

TurboGears | Coding Style

これもフレームワークでPEP8に準拠しています。名前は結構昔から聞いた事がありますが、どういう実績があるのかはちょっと分かりませんでした。

Repoze

Repoze は Zopeの各コンポーネントWSGIアプリでも使えるようにするプロジェクトだそうです。PEP8に従っています。repoze.who(http://docs.repoze.org/who/1.0/) 等は情弱な僕でも聞いた事があります。

Coding Style的なものが見つからなかった。

まとめ

調べる前は、結構Webアプリフレームワーク当たりは独自のStyleを推奨していたりして、カオスな事になってるケースもあるかと思ったんですが、ほとんどがPEP8に従っていて、PEPの影響力すごいなぁとか思いました。

いやほんと「here is the golden rule: Imitate the existing [フレームワーク名] code.」とか書いてなくてすごい。

何か他に良さそうなものがあったら教えて下しあ

hgrc を分割する

下記、記事を見て、僕もdotfiles系を整理しよう&githubで公開しようと思った時のメモ

hgrc 分割したい

  • 上の記事を見て、あーvimrcとかって分割してるのかー賢いなーとか思って、さっそく真似っこする
  • githubに上げたいので、あんまり公開したくないものは、リポジトリ管理外の所に設定ファイルを作って読み込むようにする
  • vimrcとかbashrcとかはいいけど、利用しているリポジトリ一覧を[paths]に丸っと書いてる僕としては「~/.hgrc」を分割したい!

という訳で調べたら、もうそれは アメリカ県民の人がとっくに通った道だった件。。。

「include」を使えば分割した設定ファイルを読み込む事ができるらしい。といわけでこんな風にした。

#basic settings
%include ~/.hgrc.d/.hgrc.basic

#extension settings
%include ~/.hgrc.d/.hgrc.extension

#misc settings
%include ~/.hgrc.d/.hgrc.misc

#private settings
%include ~/.dotfiles_private/.hgrc

#local settings
%include ~/.dotfiles_local/.hgrc

で gitconfig は includeみたいな事できないのか探したけど見つからず。誰か知ってたら教えてplz

今は苦し紛れにプライベートの場所に、一覧を記載・管理するシェルを書いて、それを強引に呼び出すようにしてる。

[alias]
  cl = !sh ~/.dotfiles_private/git_clone.sh

以上おわり。

余談

  • という訳で公開してみた。 https://github.com/tell-k/dotfiles
  • 思えば dotfilesの管理も github -> bitbucket -> github という感じで出戻ってて良くわからん感じ。

100,000年後の安全 (原題: Into Eternity)

というタイトルの映画を見た。放射能廃棄物の最終処分場として最初に決定されたフィンランドオンカロという施設について関係者のインタビューをメインに構成されたドキュメンタリー映画だ。

f:id:tell-k:20120218225432p:plain
Amazon.co.jp 100,000年後の安全

ドキュメンタリー映画だけど、映像が綺麗で、且つSFのような話で、割と楽しめた。と同時に本気でこんなプロジェクトが現在進行形で進んでいる事に驚いた。

以下はメモ書き。

映画

  • タイトル: 100,000年後の安全 (原題: Into Eternity)
  • 監督: マイケル・マドセン
  • 時折、監督が出てきて、意味深な事を語ったり、「未来に」この映像を見た人に向けてナレーションが入る。映像は綺麗でSFを見てるみたいだった。割とあっという間に見終わる。

内容

  • フィンランド オルキオト島 にある 放射能廃棄物 最終処分場(場ていうか洞窟)
  • オンカロ = 「隠された場所」の意味
  • 耐用年数 10万年 を想定。 (放射能廃棄物が人体に無害になるまで最低10万年かかるらしい)
    • 人類の建造物でそんな長い間もったもんなんかない
  • 以下のようなチャプター構成で話がすすむ。
    • NUCLEAR WASTE (放射性廃棄物)
    • INTERIM STRAGE (中間貯蔵)
    • THE PERMANET SOLUTION (恒久不変な解決)
    • HUMAN INTRUSION (人間の侵入)
    • WARNING THE FUTURE (未来への警告)
    • THE LAW (法律)

NUCLEAR WASTE (放射性廃棄物)

  • 現存する放射性廃棄物はの総量は30万トン近くある
  • 廃棄するために、以下のような方法も検討された
    • ロケットで太陽に突っ込む
    • 海底に沈める
    • いずれもロケットは途中での爆発、海底も海の汚染が考えられるので没
  • 放射線は無色透明で、無臭、人間の五感では感知できない。
  • ただ浴び続ければDeNA分子を破壊し、人間を死に至らしめる
  • 人体に無害になるまで10万年を要する

INTERIM STRAGE (中間貯蔵)

  • 最終処分する前は水槽に入れて管理している
  • 水が放射線を遮るから
  • 水槽で10万年保管し続けることは難しい。地上は戦争、災害、あらゆる危険がつきまとう。
  • 過去100年の内 世界大戦が2回、チェルノブイリ原発事故もあった
  • チェルノブイリの時は、放射線を含む雲がヨーロッパ全土を覆った
  • 現在の文明はエネルギーに依存している。エネルギーはいわば通貨だ。
  • 廃棄物を、恒久的に安全な方法で処分する必要がある。
  • 原子力に賛成か、反対かは問題ではない。
    • 使い続ける限り廃棄物は出続けるので、それを処分する方法が必要
  • オンカロは現在、長い長い洞窟を掘り進めてる所、作業員がどでかい穴にダイナマイトを仕掛けて掘り進めてる

THE PERMANET SOLUTION (恒久不変な解決)

  • 最終処分場を世界で最初に決めたのはフィンランド
  • フィンランドは先駆者
  • 10万年持たせないと行けないという、壮大なプロジェクト
  • フィンランドの地層は18億年前のもの => だから地下が安定していると判断した
  • 処分場は、自己完結できる形でなければいけない。
    • 自己完結とは、最終的に人間の監視が不要、放置できる状態にならなければならない。
  • 地下500m 全長4km以上の地下都市みたいな洞窟
  • 廃棄物には何重にもバリアがもうけられ、マトリョーシカのように廃棄物を格納していく。
  • 2100年に操業を停止、施設の入り口をコンクリートを覆い尽くし永遠に密閉する。
  • 密閉後は、処分場を再び埋めて、その土地で生物が生活できるレベルにまで環境を戻す。
  • 6万年後に氷河期が来ると推測してる。来たらその辺はきっとツンドラになる。その頃にはおそらく忘れられてる。

HUMAN INTRUSION (人間の侵入)

  • 人間が侵入する可能性は?
  • 侵入できる奴らはきっと放射能である事がわかるはず(確信はないけど)
  • 何かしらの埋葬地、財宝と捉えられる可能性もある。
  • ピラミッドの意義を現代人が完全には理解できないように、未来の人間もオンカロの意義を完全には理解できないだろう。
  • もしかしたら人類は原始に近い存在になってるかもしれない。掘り起こす技術があっても、何かを理解できない可能性はある。
  • 遠い未来に放射能廃棄物が黄金以上の価値を持つ可能性はある。
    • ウランや銅、プルトニウムが含まれているから。
    • そういった意味で危険だと分かっていても、掘り起こす可能性はある。

WARNING THE FUTURE (未来への警告)

  • 掘らせない手だては?
  • 一定の期間は標識などをたてて警告する事も可能だろう。ルーン石碑のように
  • 敷地内に石柱のようなものをに警告文を国連公用語6つで記載する案もある。
  • 石柱の他に、詳細な情報を記載した、文書館みたいなものも用意する。当然石で。
  • 未来の人たちは今の言葉理解できるか?
  • 理解できない可能性もある。言語よりもや絵のような普遍的な伝達手法が必要という案もある。
  • 感覚に訴える
  • 針山、立ちはだかる壁、ムンクの「叫び」のように負の感情を想起させる普遍的なもの検討している。
  • 現代も石碑はあったけど解釈するのに年月を要した。 -> それで本当に伝わる?
  • 人間は好奇心の固まり。逆に危険。
  • ノルウェーのルーン石碑、表が下になっていいて、警告文が書かれている。「不届き者が触れてはならぬ」
  • 考古学者はガン無視した
  • 我々の標識も同じ運命をたどるかもしれない。
  • 標識を残す派と、忘れ去る派という二つの意見がある。
  • どうやって忘れさせるの?
  • 処分場を忘れる事を永遠に忘れないという矛盾

THE LAW (法律)

  • 最終処分場について法制化が進んでる
  • 「君に伝える事は法律で決められている。」
    • 「何か不具合があった場合に対処してもらうためだ」
  • 法制化にあたっての三原則
    • 負担の制限 (未来に過度な負担を課さない)
    • 未来世代の安全保障(現状の安全と同程度のもの保障する)
    • 未来の世代に伝える事(最終処分について次世代に伝承する)
  • 知識の継承についての方法 -> ぶっちゃけまだ決まってない
  • 世代が代わるたびに、伝承方法を見直してもらうという案もある
  • 実質未来に情報を伝達することだけがきまってる。
  • (研究者?) そんなん旨くいく方法ないやろ。結局政府はお得意の議論をしてるだけ。
    • (インタビュアー) は?理解できないんですけど?
    • (研究者) みんなそうでしょうね(笑)
  • 法律で決めた以上、未来に伝達する責務は国に移った
  • 未来の全世代が理解できないといけないという責務
  • 文書館を造る。未来の世代は自分たちの理解できる言語で適宜アップデートが必要。
  • 3000世代先まで。。。
  • 人間の管理を必要とする = 安全が保障できない(戦争、災害、様々な危険)
  • (プロジェクト関係者) そういった情報に頼らなくても安全に保管できるのが最終処分場の存在意義なんだけど
    • (インタビュアー) じゃあなんで未来に伝達するなんて法律できたん?
    • (プロジェクト関係者) 。。。知らんがな
  • 10万年前の人類は、ネアンデルタール人。10万年後は、異なる知識、もしかしたら異なる感覚をもってる可能性がある。
  • だからやっぱ伝達するのは難しいよね。
  • (インタビュアー) 未来の奴ら、掘り返さないって思う?信頼してる?
    • (プロジェクト関係者) 未来は不確定すぎるから正直難しい。だから可能なかぎり、オンカロを人類から切り離したい

最後のほう

  • 我々にも古くからの伝説や、言い伝えがあったように、遠い未来の君にも、オンカロの話が伝説となって伝わるかもね。
  • オンカロが処分する廃棄物は、世界中のほんの一部。まだまだ必要になる。
    • そのためには地震がなさそうな、地層が安定した場所がもっと必要になる。そして君に知られないようにしなければならない。
  • 仮に20年後、中国とインドの全人口が、先進国並みに電力を必要としたら、毎日3基ずつ原子力発電所が新たに必要になる
  • 再利用にも危険はともなう
  • 無害にする研究もあるが、理論上の話で、実現は今んところ不可能
  • 廃棄物を出さずに原子力を利用する方法は現時点ではない。
  • 100年単位のスパンでみれば原子力も使い続ける事は難しい
    • ウランも枯渇する
    • ウランを巡って戦争が起こるかもしれない
    • ますます未来は不安定である。
  • 一度埋葬したら二度と掘り返してはいけない。
  • 「不確実性の下での意思決定」こそが、「放射能廃棄物処理」
  • だからこそ予測不能であることまで、皆に開示されなければならない
  • 関係者達への最後の質問「未来でこれを見てる人へのメッセージをどうぞ」
  • 「いいか!絶対に掘るな!仮に掘ったとしても絶対に近づくな!近づくなよ!近づかなければ安全だ! 」 的なメッセージだった。

印象に残ったシーン

チャプターの間に、監督が暗闇でマッチに火をつけて、火が消えるまで、意味深な事を言うシーンが幾つかあった。その中でも印象に残ったヤツを探したので乗っけておく。

 Once upon a time, man learned to master fire,
 Something no living creature had done before him. 
 Man conquered the entire world. One day, 
 he found a new fire. A fire so powerful,
 that it could never be extinguished.
 Man reveled in the thought that he now possessed the powers of the universe.
 Then in horror, he realized that his new fire could not only create,
 but also destroy.
 Not only could it burn on land, but inside all living creature,
 inside his children, the animals, all crops. Man looked around for help, but found none. 
 And so he built a burial chamber, deep in the bowels of the Earth; 
 a hiding place, for the fire to burn, into eternity

字幕はこんな感じだった。

 昔々、人は火の扱い方を学んだ
 他の生物が出来なかった事だ。
 そして世界を制服した。
 ある日新しい火を発見した。消せないほど強力な火だ。
 ヒトは宇宙の力を得たと悦に入っていた。
 そしてゾッとすることに、新しい火に破壊力がある事にきづいた。
 大地と生き物を焼き殺すことができるのだ
 自分の子供や動物 作物までも灰にする
 ヒトは助けを求めたが、助けはこなかった
 そこで地球の内部 奥深くに埋葬室を造った
 火はその場所で 人知れず永遠に眠りにつく

以上おわり。

Mac で Vim とクリップボードを共有

Vim で OSのクリップボードを共有する時は、.vimrcに 「set clipboard=unnamed」と書けば良い。

set clipboard=unnamed

これで vim上でヤンクしたものとかが、Command + V とかで張りつけできたりする。

ところが、ある日ふと家の Mac OS X(Lion)で出来てない事にきづいた。vimrcを見直してみたけど、特に問題なさそうというか、会社でも同じvimrcの設定使ってるのに旨くいってるし。。。

良くわからないのでググったら、答えが見つかった。

なんとデフォルトでインストールされてるvimだと 「-clipboard」て書いてあって、つまり無効。F○○K!!

~$ /usr/bin/vim --version
VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Jun 24 2011 23:09:22)
Compiled by root@apple.com
Normal version without GUI.  Features included (+) or not (-):
-arabic +autocmd -balloon_eval -browse +builtin_terms +byte_offset +cindent 
-clientserver -clipboard +cmdline_compl +cmdline_hist +cmdline_info +comments
..
..

vimのインストール

というわけで vimのインストールしますお。 macportsとか使い方忘れたので、情弱な僕はhomebrewでインストール。。。。と探したら無い。。。。どうやら提供されていないようだ。。。野良Formulaという選択肢も考えたけど、なんかみんなつまづいてるから危険

~$ brew search vim
macvim              vimeo-downloader    vimpager            vimpc

という訳で、素直にソースからインストール。最近のvimMercurialリポジトリを公開しているので、それを利用するのが多分一番手っ取り早い。パッチもあたってる状態なので、最新のものがインストールできる。

hg clone https://vim.googlecode.com/hg/ vim
cd vim
hg update vim73
make clean
./configure --prefix=/usr/local --enable-multibyte --enable-xim --enable-fontset --enable-rubyinterp --enable-perlinterp --enable-pythoninterp --with-features=huge --disable-selinux
make
make install

これで大体、僕が必要そうなものはインストールされる。結果はこんな感じ

$ vim --version
VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Feb 16 2012 21:56:06)
MacOS X (unix) version
Compiled by tell_k@tell-k-mackbookair.local
Huge version without GUI.  Features included (+) or not (-):
+arabic +autocmd -balloon_eval -browse ++builtin_terms +byte_offset +cindent 
-clientserver +clipboard +cmdline_compl +cmdline_hist +cmdline_info +comments
..
..

ちゃんと「+clipboard」になってますね。めでたし。めでたし。 良かったね > おれ

Mac で mkdpreview-vim を使って reST のプレビューを見る

mkdpreview-vim は Markdown記法で書かれたテキストを書きながら、別ウィンドウ上でプレビューも一緒に確認できる便利プラグイン。

Markdown以外に、reST と textile にも対応している。
これを利用してMacOS X 上で reSTructuredTextのプレビューを見てみる。

以下 プレビューを表示するまで。

環境

  • MacOS X

pyqtのインストール

  • 時間かかるんで最初にインストールしておく。
  • 面倒なのでhomebrewでまるっとインストール
$ brew install pyqt
  • Qt と sit と PyQT4 を一気にインストールするから、多分 20~30分くらい時間かかる。
  • インストールが完了したら、PyQTのある場所にパスを通す。
$ export PYTHONPATH=/usr/local/lib/python

Sphinxのインストール

  • reSTをプレビューするためには、docutilsが必要です。Sphinxはdocutilsに依存してるのでpipでインストールすると、Sphinxとdocutilsが一緒にインストールされる。
  • なんでわざわざSphinxまで入れるのかというと、Sphinxがデフォルトで持ってるディレクティブを利用したいから。
$ pip install Sphinx

vimプラグインのインストール

  • vundleを利用して、必要なプラグインをインストール。vundleが何か分からない人は下記を参照してください。
  • .vimrc に下記のように書いて、BundleInstallを実行する。
Bundle 'mattn/mkdpreview-vim'
Bundle 'mattn/webapi-vim'
  • インストールが終わったらmkdpreview-vimに同梱されてるPythonスクリプトに実行権限を与えておく。
$ chmod u+x ~/.vim/bundle/mkdpreview-vim/static/mkdpreview.py

mkdpreview.vimをいじる。

  • 「~/.vim/bundle/mkdpreview-vim/plugin/mkdpreview.vim」を開いて5行目あたりを下記のように編集する。
 #変更前
  \ "data" : join(getline(1, line('$')), "\n")

 #変更後
  \ "data" : join(getline(1, line('$')), "\n"), 
  \ "type" : 'rst'
  • 注意: 多分そのうち 正式にtypeを指定する方法は変わると思います。

追記(2012/02/13)

  • 最新版では type値に関しては、filetypeを自動で読み取って勝手に指定しくれるようになりました。上記修正は必要ありません :)
  • 下記のようになってました。
function! s:update_preview() 
  let ret = http#post('http://localhost:8081/', {
  \ "data" : join(getline(1, line('$')), "\n"),
  \ "type" : &filetype
  \})
  echo ret.content
endfunction

さぁプレビュー

  • こんなrstファイルを用意してvimで開く
.. contents:: 目次 

The Road of the Drillbits
===============================


第1章 どりるびっつの仲間
---------------------------------

.. code-block:: python

  print "The Fellowship of the Drillbits"


第2章 二つのどりるびっつ
---------------------------------

.. code-block:: python

  print "The Two Drillbits"


第3章 どりるびっつの帰還
---------------------------------

.. code-block:: python

  print "The Return of the Drillbits"
  • vimを開いて下記コマンドを実行
:MkdPreview! 
  • するとおもむろにウィンドウが立ち上がりプレビューがこんな感じでみれる!

f:id:tell-k:20120210194049p:plain

  • 以後は、「:w」で保存すれば勝手にプレビューが更新される。うまく更新されなかったら「:MkdPreview」と打つと良い。

code-blockディレクティブとか使いたい。

  • 上の画像では、「.. code-block::」の所で「Unknown directive type "code-block".」という感じで、そんなディレクティブねーよとおこられる
  • これはそもそも「.. code-block::」が、docutilsのディレクティブではなく、Sphinxに付属するディレクティブだから。Sphixはインストール済みなので、Sphinx付属のディレクティブが利くようにまたちょっといじる。

「~/.vim/bundle/mkdpreview-vim/static/plugin/mod_rst.py」というファイルを開いて下記のよう2行追加する。

  from docutils.core import publish_parts
+ from sphinx.directives.other import * 
+ from sphinx.directives.code import *
  • これでもう一度、ファイルを開き直して、「MkdPreview!」を実行すると、無事にエラーが無くなる

f:id:tell-k:20120210195550p:plain

以上おわり。

余談

  • 「.. code-block::」とか要らないという人は「mod_rst.py」を書き換えないで、且つSphinxをインストールせずに「pip install docutils」とすれば良いでしょう。
  • mattnさんが仕事はやすぎで「rstpreview-vim」作ったのにお蔵入りした。
  • PyQT 初めていれてみたけど、おもしろそうな感じ
  • もはや Markdown Preview ではないとか小さい事を気にしてはいけない
  • はてなブログ いつのまにか カテゴリ機能みたいなの動いてる

bpmappers 便利!

今日は bpmappers というライブラリが、すごく便利で感動したという事を書く

良くJSONを返すようなAPIを作るときに、オブジェクトの必要な部分だけを、辞書に変換してからJSONにするなんて事をすると思う。オブジェクトのプロパティが、数値やテキストなどリテラルなものであれば良いが、別のオブジェクトだったりすると、そもそもオブジェクトから辞書への変換は面倒くさそうだ。

これをよしなにやってくれるのがbpmappersである。詳しくはドキュメントを見てもらうとして、簡単な例を紹介しようと思う。

前提

まず SQLAlchemyで下記のようなモデルがあるとする。Entry と Comment は 1:N の関係にある。

# declare models 
class Entry(Base): 
    __tablename__ = 'entries'  
    id = Column(Integer, primary_key=True)   
    text = Column(String)  
    created_at = Column(DateTime, default=datetime.now, nullable=False) 

    comments = relationship('Comment') 

class Comment(Base):
    __tablename__ = 'comments'
    id = Column(Integer, primary_key=True)
    entry_id = Column(Integer, ForeignKey('entries.id'), nullable=False)
    text = Column(String)
    created_at = Column(DateTime, default=datetime.now, nullable=False)

このように定義すると「Entry.comments」は下記のようにCommentオブジェクトのリストとして取得できる。

# select data 
entries = Entry.query.all() 
for entry in entries:
   for comment in entry.comments: 
        print ' comment.id: ' + str(comment.id)
        print ' comment.text: ' + comment.text
        print ' comment.created_at: ' + str(comment.created_at)

この入れ子になったオブジェクト構成をbpmappersを使って簡単にJSONにしてみる。

Mapperの定義

# declare mappers
class CommentMapper(Mapper):
    id = RawField()
    entry_id = RawField()
    text = RawField()
    created_at = RawField(callback=lambda x:x.strftime('%Y-%m-%d %H:%M:%S'))
                                                                            
class EntryMapper(Mapper):
    id = RawField() 
    text = RawField() 
    created_at = RawField(callback=lambda x:x.strftime('%Y-%m-%d %H:%M:%S'))
 
    comments = ListDelegateField(CommentMapper) 

#convert to json  
print json.dumps(EntryMapper(Entry.query.all()).as_dict(), indent=2)

これで終わり。 Mapperを定義するだけ。 あとはas_dict()でよしなに辞書にしてくれる。実行結果はこんな感じ

{
  "created_at": "2012-02-08 00:06:12", 
  "text": "entry text", 
  "id": 1, 
  "comments": [
    {
      "created_at": "2012-02-08 00:06:12", 
      "text": "comment1 text", 
      "entry_id": 1, 
      "id": 1
    }, 
    {
      "created_at": "2012-02-08 00:06:12", 
      "text": "comment2 text", 
      "entry_id": 1, 
      "id": 2
    }
  ]
}

Mapperクラスを継承して、必要なフィールドを定義するだけで目的が達成できてしまった。RawFieldはオブジェクトが持つ同名のプロパティをそのままセットしてくれる。callbackも呼べるので適宜データを加工した上でセットすることも可能。

ポイント は ListDelegateField 。 リストの各要素に対するマッピング処理を別のMapperクラスに委譲できる。この例でいうと、commentsの各要素であるCommentオブジェクトのマッピング処理は、CommentMapperに任せてる。

まとめ

駆け足で説明したけど、まとめるとこんな感じ

  • Mapperを定義するだけで、オブジェクト自身のデータをゴニョゴニョしなくていいのは大変嬉しい。
  • DelegateFieldがある事で、ネストしたデータ構造でも安心して使える。
  • Modelクラスと1 : 1 になるように掛けるので、ごちゃごちゃしない

以上おわり。

おまけ

コードの断片だと分かりにくいと思うので、サンプルコードをまるっと貼付けておく。

#!/usr/bin/env python
#-*- coding:utf8 -*-

import json 
from datetime import datetime 

from sqlalchemy import create_engine, Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import scoped_session, sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base

from bpmappers import Mapper, RawField, ListDelegateField 

# create base 
engine = create_engine('sqlite://', convert_unicode=True, echo=False) 
db_session = scoped_session(sessionmaker(autocommit=False,  
                            autoflush=False,
                            bind=engine)) 
Base = declarative_base() 
Base.query = db_session.query_property()

# declare models
class Entry(Base):
    __tablename__ = 'entries' 
    id = Column(Integer, primary_key=True) 
    text = Column(String) 
    created_at = Column(DateTime, default=datetime.now, nullable=False)

    comments = relationship('Comment')

class Comment(Base): 
    __tablename__ = 'comments' 
    id = Column(Integer, primary_key=True)  
    entry_id = Column(Integer, ForeignKey('entries.id'), nullable=False)  
    text = Column(String) 
    created_at = Column(DateTime, default=datetime.now, nullable=False) 

# declare mappers
class CommentMapper(Mapper): 
    id = RawField() 
    entry_id = RawField()
    text = RawField() 
    created_at = RawField(callback=lambda x:x.strftime('%Y-%m-%d %H:%M:%S'))

class EntryMapper(Mapper): 
    id = RawField() 
    text = RawField() 
    created_at = RawField(callback=lambda x:x.strftime('%Y-%m-%d %H:%M:%S'))
    comments = ListDelegateField(CommentMapper)

# insert data 
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)

db_session.add(Entry(text='entry text'))
db_session.commit()
db_session.add(Comment(entry_id=1, text='comment1 text')) 
db_session.add(Comment(entry_id=1, text='comment2 text')) 
db_session.commit()

# select data
print '-' * 10 
entries = Entry.query.all() 
for entry in entries: 
    print 'entry.id: ' + str(entry.id)  
    print 'entry.text: ' + entry.text 
    print 'entry.creted_at: ' + str(entry.created_at) 
    for comment in entry.comments: 
        print ' ' + ('-' * 9)
        print ' comment.id: ' + str(comment.id)  
        print ' comment.text: ' + comment.text
        print ' comment.created_at: ' + str(comment.created_at)

# convert to json
print '-' * 10
print json.dumps(EntryMapper(entries).as_dict(), indent=2)