SQLAlchemy 0.5.2
メインゴールはあなたのデータベースとSQLへの考え方を変えること!
ということを目標に作られている、O'REILLYから本(ISBN:0596516142)も出てる有名O/Rマッパです。
本家のチュートリアルもWEB+DB PRESS Vol.46の記事でもSQLiteを使ってましたので、MySQL5.1で使ってみます。
準備
SQLAlchemyのインストール
普通にeasy_installで大丈夫です。これを書いている時点ではバージョン指定なしで、0.5.2がインストールされます。
easy_install sqlalchemy
セッションを作る
まずエンジンを定義して、それを元にセッションを生成〜という流れになります。
で、エンジンはsqlalchemy.create_engine関数に、RFC-1738形式のURLを渡すことで生成します。
MySQLでlocalhostのhogeデータベースに接続する場合は
という感じで。
engine = create_engine('mysql://user:password@localhost/hoge', echo=True)
echo=True としておくと実行したSQLやらを標準出力に出してくれます。デフォルトはFalseです。
sqlalchemy.orm.sessionmaker()でセッションクラスを生成します。
この戻り値がセッションのサブクラスになっているので、これをアプリケーションのグローバルスコープでやってくれとのことです。つまり、
としておけば、後はセッションがほしくなったときにその場所で
Session = sqlalchemy.orm.sessionmaker(bind=engine)
すればセッションゲットだぜ!という寸法です。クラスをオブジェクトとして利用できるPythonらしい方法だなーと。
sess = Session()
ちなみにsessionmaker()関数はキーワード引数をとり、エンジンを指定するbind以外にも
autocommit(bool型でデフォルトはFalse)やautoflushなどいろいろ指定できますようで。詳しくはこちら。
テーブル定義
テーブルはsqlalchemy.schemaパッケージのTableクラスのインスタンスとして定義します。
インスタンス化するときに、カラムの定義としてColumnクラスを、型の定義に型用のクラスを使います。この辺のものはsqlalchemy/__init__.pyでインポートされているので、特に型についてはそれを見るとどんな型があるのか一目でわかると思います。
でまぁ、こんなソースを書いて
んでこれを実行すると、
import sqlalchemy
from sqlalchemy import Table, Column, Integer, String
metadata = sqlalchemy.MetaData()
hoge_table = Table('hoges', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(50)),
Column('level', Integer, server_default='1'),
mysql_engine='MyISAM',
mysql_charset='utf8')if __name__ == '__main__':
from sqlalchemy import create_engine
engine = create_engine('mysql://user:pass@localhost/hoge', echo=True)
metadata.create_all(engine)
というSQLが実行されて
CREATE TABLE hoges (
id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(50),
level INTEGER DEFAULT '1',
PRIMARY KEY (id)
)ENGINE=MyISAM CHARSET=utf8
mysql> describe hoges;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Field | Type | Null | Key | Default | Extra |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
id | int(11) | NO | PRI | NULL | auto_increment |
name | varchar(50) | YES | NULL | ||
level | int(11) | YES | 1 |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
となりました。
Columnクラスのキーワード引数、server_defaultに値を指定しておけば、デフォルトになるんですが、カラムの型がIntegerでもstr型で指定しないとエラーになります。
MySQLの場合はエンジンやらcharsetやらありますので、mysql_ほにゃらら=ごにょごにょ という感じで指定します。
metadataは、なんか特殊なことをするときに利用するそうで、引数なしでやっておけばデフォルト値が全部使われます。
Tableクラスのインスタンスとして定義したテーブルの情報はクラスとマッピングするときに使いますので、上で書いたソースは別のファイルからimportして使えるようにしています。
ちなみにテーブルを作るだけならセッションを生成する必要はありません。
テーブルとクラスのマッピング
Tableクラスのインスタンスとして定義したテーブルを、クラスに紐付けます。
クラスは全部の属性を定義する必要は必ずしもなく、中身がpassしてるだけのクラスでも、カラム名をそのままオブジェクトの属性として使ってくれます。
マッピングには sqlalchemy.orm.mapperを使い、
とすることでマッピングできます。
sqlalchemy.orm.mapper(クラス名, テーブルインスタンス)
レコードの追加
というわけでhoge_tableのレコードをHogeクラスとして定義し、マッピング。
その後、てきとうなデータを3つインサートし、コミットしてみます。
マッピングするクラスはどうやら新スタイルクラスである必要があるみたいです。
import sqlalchemy.orm
from mytables import hoge_tableengine = sqlalchemy.create_engine('mysql://user:password@localhost/hoge', echo=True)
Session = sqlalchemy.orm.sessionmaker(bind=engine)
sess = Session()class Hoge(object):
def __init__(self, name, level=None):
self.name = name
self.level = levelsqlalchemy.orm.mapper(Hoge, hoge_table)
hoge1 = Hoge('ジャン')
sess.add(hoge1)hoge2 = Hoge('ポール', 3)
sess.add(hoge2)hoge3 = Hoge('ベルモント', 5)
sess.add(hoge3)sess.commit()
これを実行すると、
と標準出力にアウトプットされます。
2009-02-01 05:13:04,062 INFO sqlalchemy.engine.base.Engine.0x...37f0 BEGIN
2009-02-01 05:13:04,078 INFO sqlalchemy.engine.base.Engine.0x...37f0 INSERT INTO hoges (name) VALUES (%s)
2009-02-01 05:13:04,092 INFO sqlalchemy.engine.base.Engine.0x...37f0 ['\xe3\x82\xb8\xe3\x83\xa3\xe3\x83\xb3']
2009-02-01 05:13:04,171 INFO sqlalchemy.engine.base.Engine.0x...37f0 INSERT INTO hoges (name, level) VALUES (%s, %s)
2009-02-01 05:13:04,171 INFO sqlalchemy.engine.base.Engine.0x...37f0 ['\xe3\x83\x9d\xe3\x83\xbc\xe3\x83\xab', 3]
2009-02-01 05:13:04,171 INFO sqlalchemy.engine.base.Engine.0x...37f0 INSERT INTO hoges (name, level) VALUES (%s, %s)
2009-02-01 05:13:04,171 INFO sqlalchemy.engine.base.Engine.0x...37f0 ['\xe3\x83\x99\xe3\x83\xab\xe3\x83\xa2\xe3\x83\xb3\xe3\x83\x88', 5]
2009-02-01 05:13:04,187 INFO sqlalchemy.engine.base.Engine.0x...37f0 COMMIT
あら?と思うかもしれませんがテーブルのエンジンはMyISAMでも、commitしないとデータが反映されないみたい。commit()したときにSQL発行している模様です。
複数レコードを同時に投入する場合は
という感じでリストで渡した方がスマートです。
sess.add_all([hoge1, hoge2, hoge3])