概要
ここ数日Python の ORM を調査した結論。
結論
「peewee」お勧め。よほど複雑なことをしないならこれで十分。
「peewee」で機能が不足するレベルだと、「SQLAlchemy」以外の選択肢は無い。
「SQLObject」と「storm」は「peewee」の出きが良いので、調査する気力がなくなった。
2012/10/16追記:
この記事は Python のORM 調査の記事の一部となる。以下が関連記事。
ここ数日Python の ORM を調査した結論。
「peewee」お勧め。よほど複雑なことをしないならこれで十分。
「peewee」で機能が不足するレベルだと、「SQLAlchemy」以外の選択肢は無い。
「SQLObject」と「storm」は「peewee」の出きが良いので、調査する気力がなくなった。
この記事は Python のORM 調査の記事の一部となる。以下が関連記事。
参考に「SQLAlchemy Core」のサンプルを書いてみる。
ORM に関しては昨日のエントリを参照。
良い書き方でない部分もあるが、昨日の ORM とほぼ同じことをやっているサンプルが以下。SQLを細かく制御したい場合用。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
from datetime import datetime
import sqlalchemy as sa
import sqlalchemy.dialects.mysql as samy
class TestSqlAlchemy(unittest.TestCase):
def setUp(self):
# DB 接続情報 MySQL の場合デフォルトでは MySQL-python ドライバを利用する
engine = sa.create_engine(
'mysql://username:password@localhost/example',
# 詳細表示
echo=True)
metadata = sa.MetaData()
self.tweets = sa.Table(
'tweets', metadata,
sa.Column('id', samy.BIGINT(unsinged=True), primary_key=True),
sa.Column('status_id', sa.VARCHAR(length=255)),
sa.Column('from_user_id', sa.VARCHAR(length=255)),
sa.Column('text', sa.VARCHAR(length=140)),
sa.Column('created_at', sa.VARCHAR(length=50)),
sa.Column('datetime', sa.DATETIME)
)
self.conn = engine.connect()
def test_main(self):
### COUNT
# Core 版
count_sql = sa.select(
[sa.func.count(self.tweets.c.id).label('count')])
self.assertEqual(0, self.conn.execute(count_sql).fetchone()['count'])
### INSERT
sql = self.tweets.insert().values(
status_id='251298602096xxxxx1',
from_user_id='43172xxx1',
text='さんぷるでーた',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
# トランザクション制御をしないと COMMIT する
self.conn.execute(sql)
self.assertEqual(1, self.conn.execute(count_sql).fetchone()['count'])
sql = self.tweets.insert().values(
status_id='251298602096xxxxx2',
from_user_id='43172xxx2',
text='さんぷるでーた その 2',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
# トランザクション制御
trans = self.conn.begin()
self.conn.execute(sql)
self.assertEqual(2, self.conn.execute(count_sql).fetchone()['count'])
# ROLLBACK
trans.rollback()
# COMMIT は trans.commit()
self.assertEqual(1, self.conn.execute(count_sql).fetchone()['count'])
# commit か rolleback を発行するとトランザクション制御が終了する
# 制御終了後 SQL を発行すると COMMIT する
self.conn.execute(sql)
### UPDATE
# UPDATE 前件数確認
sql = self.tweets.select().where(
self.tweets.c.from_user_id == '43172xxx2')
ret = self.conn.execute(sql).fetchall()
self.assertEqual(1, len(ret))
sql = self.tweets.select().where(
self.tweets.c.from_user_id == '43172xxx3')
ret = self.conn.execute(sql).fetchall()
self.assertEqual(0, len(ret))
# UPDATE
sql = self.tweets.update().where(
self.tweets.c.from_user_id == '43172xxx2').values(
from_user_id='43172xxx3')
self.conn.execute(sql)
# UPDATE 後件数確認
sql = self.tweets.select().where(
self.tweets.c.from_user_id == '43172xxx2')
ret = self.conn.execute(sql).fetchall()
self.assertEqual(0, len(ret))
sql = self.tweets.select().where(
self.tweets.c.from_user_id == '43172xxx3')
ret = self.conn.execute(sql).fetchall()
self.assertEqual(1, len(ret))
# DELETE
self.conn.execute(self.tweets.delete())
# bulk INSERT
insert_lst = [
{
'status_id': '251298602096xxxxx3',
'from_user_id': '43172xxx3',
'text': 'さんぷるでーた その 3',
'created_at': 'Mon, 1 Oct 2012 12:33:50 +0000',
'datetime': datetime.utcnow(),
},
{
'status_id': '251298602096xxxxx4',
'from_user_id': '43172xxx4',
'text': 'さんぷるでーた その 4',
'created_at': 'Mon, 1 Oct 2012 12:33:50 +0000',
'datetime': datetime.utcnow(),
},
]
self.conn.execute(self.tweets.insert(), insert_lst)
# SELECT
sql = sa.select([self.tweets])
ret = self.conn.execute(sql)
for row in ret:
print row
ret.close()
# DELETE
self.conn.execute(self.tweets.delete())
if __name__ == '__main__':
unittest.main()
Core を直接利用するのはそれなりの理由がないとやらないと思われる。通常は ORM の機能を利用すれば良い。
この記事は Python のORM 調査の記事の一部となる。以下が関連記事。
Python の ORM を調査中。Python の ORM としてほとんど標準の的扱いの「SQLAlchemy」の最新版を調査してみる。
「SQLAlchemy」は Python の ORM ではおそらく最も高機能。
ドキュメントの充実度はかなりの物。
対応しているデータベースの数が多いのも特徴で、オープンソースのRDBだけでなく、商用データベースに関してもサポートしている。
また、各データーベースに関してデータベースドライバを数種類サポートしているのも特徴。
かなり高機能だったり複雑なアプリケーションでも、これを覚えておくと問題なく対応できる。
「SQLAlchemy 0.7 Documentation」から対象のドキュメントを見ることができる。
SQLAlchemy は Core と ORM が存在し、Core は SQL をラッピングした文法、ORM が データベースをオブジェクトして利用するための部分になる。
ドキュメントの量が多いが、とりあえず普通に使うだけなら「Object Relational Tutorial」を見るのが良い。
pipでインストールする。
pip install SQLAlchemy
自分は ORM を利用する場合あまり DDL を発行しないので、DML を中心としたサンプルとする。
今回は MySQL でデータベースとテーブル用意する。
> mysql -u root
mysql> CREATE DATABASE example DEFAULT CHARACTER SET utf8;
mysql> GRANT ALL PRIVILEGES ON example.*
-> TO username@localhost
-> IDENTIFIED BY 'password';
mysql> exit;
> mysql -u username -p example
mysql> CREATE TABLE tweets (
-> id serial PRIMARY KEY,
-> status_id VARCHAR(255) UNIQUE NOT NULL,
-> from_user_id VARCHAR(255) NOT NULL,
-> text VARCHAR(140) NOT NULL,
-> created_at VARCHAR(50) NOT NULL,
-> datetime DATETIME NOT NULL
->) engine=innodb default charset=utf8;
DML を発行するサンプルは以下。unittest で書いてある。MySQL ドライバはMySQL-pythonを利用する。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
from datetime import datetime
from sqlalchemy import (
create_engine,
Column,
VARCHAR,
DATETIME,
)
from sqlalchemy.dialects.mysql import BIGINT
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Tweets(Base):
__tablename__ = 'tweets'
# unsinged=True を利用する場合
# sqlalchemy.dialects.mysql.BIGINT を利用する
id = Column(BIGINT(unsinged=True), primary_key=True)
# length は必須ではない
status_id = Column(VARCHAR(length=255))
from_user_id = Column(VARCHAR(length=255))
text = Column(VARCHAR(length=140))
created_at = Column(VARCHAR(length=50))
datetime = Column(DATETIME)
# __init__ や __repr__ を定義すると便利だが、省略
class TestSqlAlchemy(unittest.TestCase):
def setUp(self):
# DB 接続情報 MySQL の場合デフォルトでは MySQL-python ドライバを利用する
engine = create_engine(
'mysql://username:password@localhost/example',
# 詳細表示
echo=True)
# session を作成する
# session は面倒なようで、用途によっては非常にありがたかったりする
Session = sessionmaker(bind=engine)
self.session = Session()
def test_main(self):
# SELECT
self.assertEqual(0, self.session.query(Tweets).count())
# INSERT
tweet = Tweets(
status_id='251298602096xxxxx1',
from_user_id='43172xxx1',
text='さんぷるでーた',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
# インスタンスを生成しただけでは SQL 文は発行されない
self.assertEqual(0, self.session.query(Tweets).count())
# session に追加
self.session.add(tweet)
# add で SQL 文は発行されるが COMMIT はされない
# デフォルトは autocommit=False
self.assertEqual(1, self.session.query(Tweets).count())
tweet = Tweets(
status_id='251298602096xxxxx2',
from_user_id='43172xxx2',
text='さんぷるでーた その 2',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
self.session.add(tweet)
self.assertEqual(2, self.session.query(Tweets).count())
# COMMIT を発行して確定
self.session.commit()
self.assertEqual(1, self.session.query(Tweets).filter(
Tweets.from_user_id == '43172xxx2').count())
self.assertEqual(0, self.session.query(Tweets).filter(
Tweets.from_user_id == '43172xxx3').count())
# UPDATE
for tweet in self.session.query(Tweets).filter(
Tweets.from_user_id == '43172xxx2'):
tweet.from_user_id = '43172xxx3'
self.assertEqual(0, self.session.query(Tweets).filter(
Tweets.from_user_id == '43172xxx2').count())
self.assertEqual(1, self.session.query(Tweets).filter(
Tweets.from_user_id == '43172xxx3').count())
# DELETE
for tweet in self.session.query(Tweets).all():
self.session.delete(tweet)
self.assertEqual(0, self.session.query(Tweets).count())
# COMMIT を発行して確定
self.session.commit()
# bulk INSERT
insert_lst = [
{
'status_id': '251298602096xxxxx3',
'from_user_id': '43172xxx3',
'text': 'さんぷるでーた その 3',
'created_at': 'Mon, 1 Oct 2012 12:33:50 +0000',
'datetime': datetime.utcnow(),
},
{
'status_id': '251298602096xxxxx4',
'from_user_id': '43172xxx4',
'text': 'さんぷるでーた その 4',
'created_at': 'Mon, 1 Oct 2012 12:33:50 +0000',
'datetime': datetime.utcnow(),
},
]
# 実行(ORMの場合は以下の書き方をする。Core だともっと綺麗に書ける)
self.session.execute(Tweets().__table__.insert(), insert_lst)
self.assertEqual(2, self.session.query(Tweets).count())
# COMMIT しないと ROLLBACK
if __name__ == '__main__':
unittest.main()
決して使い易いとは言えないが、用途によっては本当に便利。
また、Oracleなど商用データベースをPythonから利用する場合は、実質選択肢がこれしかない。
目的をちゃんと考えて使うと良い。
Core と ORM が存在するので、上手く使い分ける必要がある。
今回のサンプルだと bulk insert の部分を ORM だけで書いているため、かなり無理矢理な感じになっているが、Core を利用すると bulk insert はもうすこし綺麗に書ける。
速度は用途によって多少異なるが、ORM だけの利用でも結構速い。ただ本気で速度を求める場合は Core の機能を利用した方が良いかもしれない。
今回のサンプルを書くために、多少ネットの情報を検索したが、古い情報が多い。
しかも、Core と ORM の違いを理解していない解説が多い印象。
ちゃんと使いこなすには、本家のドキュメントを読む必要がある。
この記事は Python のORM 調査の記事の一部となる。以下が関連記事。
Python の ORM を調査中。今回はWebアプリケーション作成で比較的利用されている「Django」内蔵のORMを調査してみる。
「Django」はPythonで代表的なフルスタックフレームワーク。
結構なんでも揃い、かつプラグイン機構が充実しているので、拡張もしやすい。
ライセンスは BSD。Python 3 には今の所対応していない。
対応RDBは MySQL、Oracle、PostgreSQL、SQLite。
本来は ORM だけ独立で使うための物ではないかもしれないが、今回は ORM だけを調査したい。
「Django ドキュメント」から日本語のドキュメントが読める。
今回のようにORMだけ独立で利用したい場合等は公式ドキュメント(英語)の「Writing custom django-admin commands」に書いてある。使い方は昨日書いたので参照。
pip でインストールする。
pip install django
自分は ORM を利用する場合あまり DDL を発行しないので、DML を中心としたサンプルとする。
今回は MySQL でデータベースとテーブル用意する。
> mysql -u root
mysql> CREATE DATABASE example DEFAULT CHARACTER SET utf8;
mysql> GRANT ALL PRIVILEGES ON example.*
-> TO username@localhost
-> IDENTIFIED BY 'password';
mysql> exit;
> mysql -u username -p example
mysql> CREATE TABLE tweets (
-> id serial PRIMARY KEY,
-> status_id VARCHAR(255) UNIQUE NOT NULL,
-> from_user_id VARCHAR(255) NOT NULL,
-> text VARCHAR(140) NOT NULL,
-> created_at VARCHAR(50) NOT NULL,
-> datetime DATETIME NOT NULL
->) engine=innodb default charset=utf8;
次に付属のツール「django-admin.py」を利用してプロジェクトの雛形を作成する。
「django-admin.py」は pip 等でインストールすると、適切な場所にインストールされるのでパスを通しておくと良い。
以下のように利用する。
django-admin.py startproject mysite
データベースの設定は「settings.py」で行なう。HOST、PORT は初期設定のままなので、今回は空にしておく。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql.',
'NAME': 'example',
'USER': 'username',
'PASSWORD': 'password',
'HOST': '',
'PORT': '',
}
}
「settings.py」には他にも以下のような設定をした。
TIME_ZONE = 'Asia/Tokyo'
LANGUAGE_CODE = 'ja-JP'
USE_TZ = False
次にアプリケーションを作成する。
python manage.py startapp example
「management/commands」ディレクトリを作成する。
mkdir -p example/management/commands
touch example/management/__init__.py
touch example/management/commands/__init__.py
「settings.py」の「INSTALLED_APPS」にアプリケーションを有効化するために追加する。
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
# 以下を追加
'example',
)
「models.py」を以下のように記述。(実際には __unicode__ 等を書くが省略)
from django.db import models
class Tweets(models.Model):
status_id = models.CharField(max_length=255)
from_user_id = models.CharField(max_length=255)
text = models.CharField(max_length=140)
created_at = models.CharField(max_length=50)
datetime = models.DateTimeField()
class Meta:
db_table = 'tweets'
「example/management/commands」以下にスクリプトを作成。sample.py」の名前で以下のように作成。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.core.management.base import BaseCommand
from datetime import datetime
from example.models import Tweets
class Command(BaseCommand):
def handle(self, *args, **options):
# 基本的に autocommit=True
tweet = Tweets(
status_id='251298602096xxxxx1',
from_user_id='43172xxx1',
text=u'さんぷるでーた',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
# インスタンスの生成では INSERT しない
self.assertEqual(0, Tweets.objects.count())
# save メソッドで INSERT を発行
tweet.save()
self.assertEqual(1, Tweets.objects.count())
tweet = Tweets(
status_id='251298602096xxxxx2',
from_user_id='43172xxx2',
text=u'さんぷるでーた その 2',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
tweet.save()
self.assertEqual(2, Tweets.objects.count())
# UPDATE 前の確認
self.assertEqual(1, Tweets.objects.filter(
from_user_id='43172xxx1').count())
self.assertEqual(1, Tweets.objects.filter(
from_user_id='43172xxx2').count())
self.assertEqual(0, Tweets.objects.filter(
from_user_id='43172xxx3').count())
# UPDATE
tweet = Tweets.objects.get(from_user_id='43172xxx2')
tweet.from_user_id = '43172xxx3'
# COMMIT
tweet.save()
# UPDSTE 後の確認
self.assertEqual(0, Tweets.objects.filter(
from_user_id='43172xxx2').count())
self.assertEqual(1, Tweets.objects.filter(
from_user_id='43172xxx3').count())
# bulk INSERT
insert_lst = [
Tweets(
status_id='251298602096xxxxx3',
from_user_id='43172xxx3',
text=u'さんぷるでーた その 3',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
),
Tweets(
status_id='251298602096xxxxx4',
from_user_id='43172xxx4',
text=u'さんぷるでーた その 4',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
),
]
Tweets.objects.bulk_create(insert_lst)
self.assertEqual(4, Tweets.objects.count())
# DELETE
for tweet in Tweets.objects.all():
tweet.delete()
# DELETE 確認
self.assertEqual(0, Tweets.objects.count())
def assertEqual(self, first, second):
"""
テスト用の簡易関数
"""
if first == second:
print '・',
else:
print 'NG',
実行は以下のようにする。
python manage.py sample
DjangoのORMは確かに良くできている。Djangoを普段利用している人には便利だろう。
ただ、速度はあまり早くないので、速度をそれほど必要としないような管理ツール等に向いていると思われる。
この記事は Python のORM 調査の記事の一部となる。以下が関連記事。
Python の ORM を調査中。今回は小さいサイズで使いやすそうな「peewee」を調査してみる。
「peewee」は「a little orm」と記述されているように、小さいサイズのORM。
ライセンスはMIT。Python 3 には今の所対応していない。
対応RDBは、PostgreSQL、MySQL、SQLite3で、商用データベースには対応していない。
「peewee documentation」にドキュメントがある。機能は網羅されたドキュメントになっている。
とりあず入門するなら「Peewee Cookbook」を読むと良い。
pipでインストールする。
pip install peewee
自分は ORM を利用する場合あまり DDL を発行しないので、DML を中心としたサンプルとする。
今回は MySQL でデータベースとテーブル用意する。
> mysql -u root
mysql> CREATE DATABASE example DEFAULT CHARACTER SET utf8;
mysql> GRANT ALL PRIVILEGES ON example.*
-> TO username@localhost
-> IDENTIFIED BY 'password';
mysql> exit;
> mysql -u username -p example
mysql> CREATE TABLE tweets (
-> id serial PRIMARY KEY,
-> status_id VARCHAR(255) UNIQUE NOT NULL,
-> from_user_id VARCHAR(255) NOT NULL,
-> text VARCHAR(140) NOT NULL,
-> created_at VARCHAR(50) NOT NULL,
-> datetime DATETIME NOT NULL
->) engine=innodb default charset=utf8;
次に付属のツール「pwiz.py」を利用してテーブルと関連したクラスを生成する。「pwiz.py」は pip 等でインストールすると、適切な場所にインストールされるのでパスを通しておくと良い。
以下のように利用する。
pwiz.py -H localhost -u username -P password -e mysql example > example_model.py
以下のようなソースが生成されるが、気に入らない点がある。
from peewee import *
database = MySQLDatabase('example', **{'passwd': 'password', 'host': 'localhost', 'user': 'username'})
class UnknownFieldType(object):
pass
class BaseModel(Model):
class Meta:
database = database
class Tweets(BaseModel):
created_at = CharField()
datetime = DateTimeField()
from_user = CharField(db_column='from_user_id')
id = BigIntegerField()
status = CharField(db_column='status_id')
text = CharField()
class Meta:
db_table = 'tweets'
DML を発行するサンプルは以下。unittest で書いてある。MySQL ドライバはMySQL-pythonを必要とする。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
from datetime import datetime
from example_model import (
database,
Tweets,
)
class TestPeewee(unittest.TestCase):
def setUp(self):
database.connect()
# デフォルトは autocommit=True
database.set_autocommit(False)
def test_main(self):
# SELECT
self.assertEqual(0, Tweets.select().count())
# INSERT
# 日本語に u 付ける
tweet = Tweets(
status='251298602096xxxxx1',
from_user='43172xxx1',
text=u'さんぷるでーた',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
# インスタンスの生成では INSERT しない
self.assertEqual(0, Tweets.select().count())
# save メソッドで INSERT を発行
# autocommit=True の場合は COMMIT 発行
tweet.save()
self.assertEqual(1, Tweets.select().count())
tweet = Tweets(
status='251298602096xxxxx2',
from_user='43172xxx2',
text=u'さんぷるでーた その 2',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
tweet.save()
self.assertEqual(2, Tweets.select().count())
# COMMIT すると確定
database.commit()
self.assertEqual(1, Tweets.select().where(
from_user='43172xxx1').count())
self.assertEqual(1, Tweets.select().where(
from_user='43172xxx2').count())
self.assertEqual(0, Tweets.select().where(
from_user='43172xxx3').count())
# UPDATE
query = Tweets.update(from_user='43172xxx3').where(
from_user='43172xxx2')
# sql メソッドで発行される SQL 文を確認可能
self.assertEqual(('UPDATE `tweets` SET `from_user_id`=%s '
'WHERE `from_user_id` = %s',
[u'43172xxx3', u'43172xxx2']),
query.sql())
# SQL文発行
query.execute()
self.assertEqual(0, Tweets.select().where(
from_user='43172xxx2').count())
self.assertEqual(1, Tweets.select().where(
from_user='43172xxx3').count())
# COMMIT すると確定
database.commit()
# DELETE
for tweet in Tweets.select():
tweet.delete_instance()
self.assertEqual(0, Tweets.select().count())
# COMMIT すると確定
database.commit()
if __name__ == '__main__':
unittest.main()
今回調査した時間は長くないので以下が疑問として残っている。
簡単で、使い易いと感じた。
Model の自動生成ツールが付いているので、データベースの変更にも追随が容易だろう。
簡易にベンチを取ったが、速度は比較的速い。
簡単な物だったり、商用データベースを利用するのでなければ、これで十分な感じ。
この記事は Python のORM 調査の記事の一部となる。以下が関連記事。
Python の ORM を調査中。最初に自分が良く利用している「Elixir」の使い方から。
「Elixir」は「SQLAlchemy」のラッパ。気軽にSQLAlchemyの機能を使いたい場合に便利。
SQLAlchemyのラッパなので、オープンソースのデータベースは無論、商用データベースにも多数に対応しているのが特徴。
残念な事にpypi 上の最終リリースが2009-11-16で、最新の SQLAlchemy を利用すると 多少の不具合が存在しているので、手元では改造バージョンを利用したりしている。
「公式サイト」にそれなりにドキュメントが存在する。
部分的に「SQLAlchemy」ドキュメント参照になっている部分があるが、SQLAlchemyのドキュメントは多すぎる。
入門であれば「Elixirのチュートリアル」を見ると大体の機能はわかるはず。
pipでインストールする。
pip install Elixir
自分は ORM を利用する場合あまり DDL を発行しないので、DML を中心としたサンプルとする。
今回は MySQL でデータベースとテーブル用意する。
> mysql -u root
mysql> CREATE DATABASE example DEFAULT CHARACTER SET utf8;
mysql> GRANT ALL PRIVILEGES ON example.*
-> TO username@localhost
-> IDENTIFIED BY 'password';
mysql> exit;
> mysql -u username -p example
mysql> CREATE TABLE tweets (
-> id serial PRIMARY KEY,
-> status_id VARCHAR(255) UNIQUE NOT NULL,
-> from_user_id VARCHAR(255) NOT NULL,
-> text VARCHAR(140) NOT NULL,
-> created_at VARCHAR(50) NOT NULL,
-> datetime DATETIME NOT NULL
->) engine=innodb default charset=utf8;
DML を発行するサンプルは以下。unittest で書いてある。MySQL ドライバはMySQL-pythonを利用する。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
from datetime import datetime
from elixir import (
Entity,
using_options,
setup_all,
metadata,
session,
)
class Tweets(Entity):
# autoload を True にすると DB から定義を自動読み込み
using_options(tablename='tweets', autoload=True)
class TestElixir(unittest.TestCase):
def setUp(self):
# DB 接続情報 MySQL の場合デフォルトでは MySQL-python ドライバを利用する
metadata.bind = 'mysql://username:password@localhost/example'
# 詳細表示
metadata.bind.echo = True
setup_all()
def test_main(self):
# SELECT
self.assertEqual(0, Tweets.query.count())
# INSERT
# autocommit=False になる
Tweets(
status_id='251298602096xxxxx1',
from_user_id='43172xxx1',
text='さんぷるでーた',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
# この場合インスタンスが生成される度に INSERT が発行されるが、COMMITはされない。
self.assertEqual(1, Tweets.query.count())
Tweets(
status_id='251298602096xxxxx2',
from_user_id='43172xxx2',
text='さんぷるでーた その 2',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
# コミットすると確定
# session.commit()
# commit しなくても件数は取れる
self.assertEqual(2, Tweets.query.count())
self.assertEqual(1, Tweets.query.filter_by(from_user_id='43172xxx1').count())
self.assertEqual(1, Tweets.query.filter_by(from_user_id='43172xxx2').count())
self.assertEqual(0, Tweets.query.filter_by(from_user_id='43172xxx3').count())
# UPDATE
for tweet in Tweets.query.filter_by(from_user_id='43172xxx2'):
tweet.from_user_id = '43172xxx3'
self.assertEqual(0, Tweets.query.filter_by(from_user_id='43172xxx2').count())
self.assertEqual(1, Tweets.query.filter_by(from_user_id='43172xxx3').count())
# DELETE
for tweet in Tweets.query.all():
tweet.delete()
self.assertEqual(0, Tweets.query.count())
# bulk INSERT
insert_lst = [
{
'status_id': '251298602096xxxxx3',
'from_user_id': '43172xxx3',
'text': 'さんぷるでーた その 3',
'created_at': 'Mon, 1 Oct 2012 12:33:50 +0000',
'datetime': datetime.utcnow(),
},
{
'status_id': '251298602096xxxxx4',
'from_user_id': '43172xxx4',
'text': 'さんぷるでーた その 4',
'created_at': 'Mon, 1 Oct 2012 12:33:50 +0000',
'datetime': datetime.utcnow(),
},
]
# Elixir 0.7.1 は以下のロジックだと autocommit=True なので注意
Tweets.table.insert().execute(insert_lst)
if __name__ == '__main__':
unittest.main()
速度に関しては、「metadata.bind.echo」を「False」にして簡易に計測してみたが、ドライバ直接利用するのに比較すると、やはり遅くなる。また、メモリもそれなりに利用する。
本気で速度を求める場合は、ORMの利用を考慮しない方が良いだろう、
使いごこちとしては、「autoload=True」等の機能が非常に便利。
また、いざとなったら、SQLAlchemy の機能が利用できるので、ほとんどの場合、機能が不足することはない。
SQLAlchemy の機能を利用しているため、UPDATE、DELETEの発行が、SELECT してからでないとできないのは、やや不便かもしれないがORMだと比較的一般的な動作と思われる。
最近メンテナンスされていないは、懸念事項になる。
この記事は Python のORM 調査の記事の一部となる。以下が関連記事。
Python にはいくつか RDB のための ORM があるので現在時点で見つけた物の一覧表を作成してみた。
今回見つけた物で、ちゃんと利用されていそうな物は以下。
| リンク | ライセンス | PofEAA | 対応RDB | バージョン | Python3対応 | 概要 |
|---|---|---|---|---|---|---|
| peewee | MIT | ActiveRecord | PostgreSQL,MySQL,SQLite3 | 1.0.0(2012-08-26) | × | 比較的ライトなORM。 |
| Django | BSD | ActiveRecord | MySQL,Oracle,PostgreSQL,SQLite3 | 1.4.1(2012-07-30) | × | ORMを含むWebアプリケーションフレームワーク。ORMの出来が良く、単体でも利用できる。 |
| SQLAlchemy | MIT | DataMapper | DB2,ODBC,Oracle,PostgreSQL,MS SQL Server,MySQL,SQLite3,Sybase等(Supported Databases参照) | 0.7.8(2012-06-17) | ◯ | 多くのRDBに対応。非常に高機能。 |
| SQLObject | LGPL | ActiveRecord | PostgreSQL,MS SQL Server,MySQL,SQLite3,Sybase | 1.3.1(2012-05-25) | × | SQLAlchemyと比較されるORM。昔は良く利用されていたが、最近はSQLAlchemyの方が利用されている印象。 |
| storm | LGPL | ActiveRecord | PostgreSQL,MySQL,SQLite3 | 0.19(2011-10-05) | × | Ubuntuの開発支援をしているCanonical社が作成している比較的ライトなORM。 |
| Elixir | MIT | ActiveRecord | SQLAlchemyと同じ | 0.7.1(2009-11-16) | ◯ | SQLAlchemyをActiveRecordパターンで使うためのラッパ。結構便利だが、最近はメンテナンスされていない。 |
自分は普段はSQLAlchemyを主に利用していて、目的によってはElixirを改造した物を利用している。それ以外のORMをほとんど利用した事がないので、今回探したORMに関して機能を調査してみようと思う。
それぞれのソースとドキュメントを簡単に読んだが、stormとpeeweeは比較的利用される場面が似ている印象。stormはそれほど使いやすそうではない感じ。peeweeの方が簡単そう。
SQLObjectは、けっこう使い易そうだが、機能が多いのにドキュメントがいまいちな印象。
DjangoのORMはDjangoのバックエンドとして利用することが想定されているが、出来は良さげなので、すべてを極力一つのフレームワークの枠組みでやりたい人には向いているかもしれない。
この記事の後で各ORMを調査した記事を書いた。
Powered by Blogger. DownRight Blogger Theme v3.0 created by (© 2007) Thur Broeders