SQLAlchemyのよろこび
SQLAlchemy はMichael Bayerによって開発中の新しいデータベースライブラリです。TurboGearsにおける主な利用法は、ORMレイヤーにおけるSQLObjectの置換です。SQLAlchemyはSQLObject以上の数々のアドバンテージを持っています:
- 整数でないプライマリキーのサポート
- 複合(マルチカラム)プライマリキー
- 任意のselectをオブジェクトにマップできる
- いくつかのテーブルを一つのオブジェクトにマップできる(その逆も可能)
- 作業単位のコンセプトに基づいて、可能な限りクエリを効率的に実行する
もちろんいくつかのSQLAlchemyを使うことでのマイナス点もあります:
- 宣言とマッピングがSQLObjectよりも冗長
- SQLObject?の方がプロダクトとして長く使われている
- 開発途中であり、APIがリリースポイント間で変更される可能性がある
- いくつかのTurboGearsの機能はSQLObjectでのみ利用可能
これらのマイナス点にもかかわらず、ある程度のTurboGearsの開発者はそのプロジェクトでSQLAlchemyを使っており、将来的には公式なORMとなるかもしれません。
TurboGears?のSQLAlchemyサポート
動作するもの:
- 自動トランザクション
- クイックスタート
- Identity
動作しないもの:
- Catwalk
- Model Designer
- fastdata
SQLAlchemyをゲットする
cheeseshop経由で最新版を得ることができます:
easy_install SQLAlchemy
また SQLAlchemy download ページから得ることもできます。 プロジェクトドキュメント は非常に徹底しており、 sql construction のページや basic data mapping 、そして advanced data mapping は特に読んでみたくなるでしょう。
TurboGears?でSQLAlchemyを使う
SQLAlchemyを使ってみる一番シンプルな方法は、新しいプロジェクトを --sqlalchemy を使ってクイックスタートしてみることです:
tg-admin quickstart --sqlalchemy
これによって model.py がSQLAlchemyを使うように正しくセットされます。またidentityを使うかどうかのプロンプトでyesを選ぶと、ActiveMapperバージョンのidentityテーブルが生成されます。適切なデータベース接続をセットするように sqlalchemy.dburi を編集した後に、データベーススキーマを初期化するために以下を実行する必要があります:
tg-admin sql create
もしあなたがSQLObjectからSQLAlchemyにプロジェクトを変換しようとしているならば、新しいプロジェクトをクイックスタートし、identityテーブルを一つ一つコピーすることをおすすめします。
ActiveMapper?について
ActiveMapper?は標準のSQLAlchemyマッパの最上位となる軽量な宣言型レイヤーです。これによってSQLObjectでやるようにクラスを宣言することができますが、少々文法は異なります。
不幸にもActiveMapperに関する公式なドキュメントは存在しませんが、 ActiveMapper? Crash Course を参照するとよいでしょう。
assign_mapper() を使う
ActiveMapper?のただ一つの問題は、少なくとも現時点では、特に primaryjoin や secondaryjoin オプションについて、SQLAlchemyインタフェイスすべてをexposeすることができないということです。これが何らかの高度なマッピングをするときの唯一の問題ですが、この問題が起こるときっとあなたの頭を悩ませることでしょう。さらにActiveMapperとActiveMapperでないテーブルのjoinはいつも動くわけではありません。もしテーブルをそれ自身に関連づけることがわかっているならば、それをSQLAlchemyの文法で assign_mapper() を使って宣言したくなるでしょう。
assign_mapper はもう一つのSQLAlchemyの拡張です。この拡張はマップされたクラスオブジェクトに、SQLObjectやActiveMapperオブジェクトのように振る舞うようにするモンキーパッチです。これはデフォルトのSQLAlchemyマッパと違って、クラスに mapping 属性を追加するだけです。ですので myclass.select ではなく myclass.mapping.select() する必要があります。
active_mapper() はコンテキストパラメータを取ります。TurboGearsのコンテキストを使いたくなるでしょう。そういう場合は以下のようにします:
TurboGears?の metadata と session を使う:
from turbogears.database import metadata, session
session.context を使ってそのコンテキストで得る:
active_mapper(session.context, table_class, TargetClass)
Tips
デバッグ
dev.cfg ファイルで sqlalchemy.echo を True にすれば、データベースで実行されたクエリを見ることができます。
カラムのタイプをカスタマイズする
SQLObject?はPythonにデータベース変換をさせるためにformencodeを使いますが、SQLAlchemyはそのカラムデータタイプの定義を通して変換を行います。ここではSQLAlchemyのカラムデータタイプをどのように使うかを示します。
以下では二つの例を示しています。一つはシステムのタイムスタンプを表す数字をPythonの datetime に変換しています。もう一つはIPv4アドレスを整数と8ビット間で変換します。 convert_bind_param はデータベース へ のもので convert_result_value はデータベース から のものであることを覚えておいてください。
TIMESTAMP カラムタイプを作成します:
import time
from datetime import datetime
class TIMESTAMP(Numeric):
# to the database
def convert_bind_param(self,value,engine):
return super(TIMESTAMP,self).convert_bind_param(time.mktime(value.timetuple()),engine)
# from the database
def convert_result_value(self,value,engine):
return datetime.fromtimestamp(super(TIMESTAMP,self).convert_result_value(value,engine))
IPV4 カラムタイプを作成します:
import struct
from socket import inet_aton, inet_ntoa, error as socket_error
class IPv4AddrTypeError(TypeError):
def __init__(self, addr):
self.addr = addr
def __str__(self):
return "Illegal IPv4 address '%s'" % self.addr
class IPV4(Numeric):
# to the database
def convert_bind_param(self,value,engine):
try:
return super(IPV4,self).convert_bind_param(struct.unpack('!L',inet_aton(value))[0],engine)
except socket_error:
raise IPv4AddrTypeError(value)
# from the database
def convert_result_value(self,value,engine):
return inet_ntoa(struct.pack('!L',super(IPV4,self).convert_result_value(value,engine))
さらなる開発
Evan RossonはGoogle Summer of Code 2006で、コントロールスキーママイグレーションを可能にするツールを提供する Migrate プロジェクトを作りました。

