2012年10月9日

Python の ORM 調査:SQLAlchemy Core編

概要

参考に「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 の機能を利用すれば良い。

2012/10/16追記:

この記事は Python のORM 調査の記事の一部となる。以下が関連記事。

blog comments powered by Disqus