Source

mydocument / docs / sqlalchemy.txt

Full commit
SQLAlchemyを始めよう
=========================================

SQLAlchemyの特徴
-----------------------------------------

* データマッパー
* Unit of Work
* DSL

データマッパー
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

一番普及していると思われるO/Rマッパーはアクティブレコードと呼ばれるアーキテクチャ。
クラスとテーブルを1:1で割り当てることを前提としている。
SQLAlchemyが採用しているデータマッパーアプローチでは、クラスとテーブルのマッピングは前提となっていないため、自分で割り当てることになる。
その分、2テーブルから1クラスに割り当てたり、1テーブルを2クラスに分割したり、クエリをクラスに割り当てるなど、柔軟なマッピングが可能である。

Unit of Work
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

処理中の状態を記録し、DBへの反映は最後にまとめて行う。
一意マッピング

DSL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


基本
----------------------------------------

テーブルとマッピング
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
データマッパーはテーブルとクラスを明示的にマッピングできる。
`sqlalchemy` では、 `Table` オブジェクトを作成して、データスキーマを表現する。

>>> from sqlalchemy import *
>>> meta = MetaData()
>>> employee_table = Table("employee", meta,
...     Column('employee_id', Integer, primary_key=True),
...     Column('first_name', Unicode(20)),
...     Column('last_name', Unicode(20)),
...     Column('birthday', Date),
... )

>>> from sqlalchemy.orm import *
>>> class Employee(object):
...     pass
>>> employee_mapper = mapper(Employee, employee_table)


宣言的なマッピング
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

いかにデータマッパーを使うとはいえ、クラスとテーブルを直接マッピングすることが多くある。
declarativeベースの宣言方法により、クラスとテーブルを一度に定義可能である。

>>> from sqlalchemy.ext.declarative import declarative_base
>>> Base = declarative_base()
>>> class Company(Base):
...     __tablename__ = 'company'
...     company_id = Column(Integer, primary_key=True)
...     name = Column(Unicode(255))

関連マッピング
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

多くのモデルは関連を持つ。
1:多のマッピングが主になるが、1:1, 多:多マッピングも使われる。

1:多マッピングの場合は直接関連先を参照する。
多:多マッピングの場合は、中間テーブルを用意する。

>>> del Employee
>>> del employee_table
>>> class Employee(Base):
...     __tablename__ = 'employee'
...     employee_id = Column(Integer, primary_key=True)
...     first_name = Column(Unicode(20))
...     last_name = Column(Unicode(20))
...     birthday = Column(Date)
...     company_id = Column(Integer, ForeignKey('company.company_id'))
...     company = relationship('Company', backref='employees')

データベースへの接続
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

通常、`engine` と呼ばれる接続情報のオブジェクトを利用する。
この接続情報は、実行時に直接指定したり、メタデータやセッションに直接バインドしてしまう方法がある。
以下の例は、メタデータに関連するテーブルをすべて作成する際に、引数として接続情報を渡している。

>>> engine = create_engine('sqlite:///')
>>> Base.metadata.create_all(bind=engine)


Session
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DBへの反映はSessionを通して行われる。

>>> Session = sessionmaker(bind=engine)

Sessionのcommitメソッド

また、Sessionは多くのオプションを持つため、session_makerでオプション指定したファクトリを作成するのが一般的である。

Webアプリケーションなど、マルチスレッド環境で実行する場合はい、スレッドセーフなセッションファクトリとして、scoped_sessionを利用する。

Sessionのオプション

SQL Expression
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

継承
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RDBでの継承を実装する方法は3通りある。
SQLAlchemyでは、これら3通りの方法をすべてサポートしている。

クエリマッピングや導出マッピング
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


ケーススタディ
-----------------------------------------

ロールパーミッションベースのACLをサポートしたCMSを作成する。

>>> from sqlalchemy import *
>>> from sqlalchemy.orm import *
>>> from sqlalchemy.ext.declarative import declarative_base
>>> metadata = MetaData()
>>> Base = declarative_base(metadata=metadata)

`User` モデル
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>> class User(Base):
...     __tablename__ = 'cms_user'
...     user_id = Column(Integer, primary_key=True)
...     username = Column(String(255), unique=True)
...     password = Column(String(255))

`Document` モデル
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>> class Document(Base):
...     __tablename__ = 'cms_document'
...     document_id = Column(Integer, primary_key=True)
...     name = Column(String(255), unique=True)
...     title = Column(Unicode(255))
...     contents = Column(UnicodeText)
...     owner_id = Column(Integer, ForeignKey('cms_user.user_id'))
...     owner = relationship('User', backref='documents')

>>> engine = create_engine('sqlite:///')
>>> Base.metadata.create_all(bind=engine)
>>> Session = sessionmaker(bind=engine)
>>> session = Session()
>>> user = User()
>>> session.add(user)
>>> session.commit()
>>> user.user_id
1
>>> doc = Document()
>>> doc.title = u"最初のドキュメント"
>>> doc.owner = user
>>> session.commit()
>>> del user, doc
>>> session = Session()
>>> user = session.query(User).one()
>>> len(user.documents)
1
>>> user.documents[0].title == u"最初のドキュメント"
True


`MediaDocument` モデル
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`Document` のサブクラスで、メディアファイルを持つ。


`Role` モデル
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`Permisson` モデル
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

WSGIアプリケーション
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~




複数DBへの対応
---------------------------------------------

マスタスレーブ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

垂直分割
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

水平分割
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

混合
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~