Sql alchemy bpstyle_4

Post on 26-Jan-2015

127 views 8 download

description

 

Transcript of Sql alchemy bpstyle_4

SQLAlchemyBPStyle #4小田切 aodag 篤

 

アクティブレコードとデータマッパーSQLAlchemyデータマッピング宣言的な方法関連継承Session

アクティブレコードとデータマッパー

アクティブレコードクラスとテーブルを1対1に割り当てるマッピング(オブジェクトと行が対応する)簡単融通は利かない多対多関連ではマッピングされないテーブルが存在する

データマッパークラスに対するテーブルマッピングを指定する柔軟性が高いちょっと面倒多対多関連で関連属性もマッピング可能

SQLAlchemy

データマッパータイプのORマッパーSQLをPythonオブジェクトで構築Unit of workパターン

対応データベース

SQLiteMySQLPostgreSQLFirebirdOracleSQL ServerDB2

など

データマッピング(1) スキーマ定義

person_table = Table("person", meta, Column("person_id", Integer, primary_key), Column("first_name", Unicode(20)), Column("last_name", Unicode(20)), Column("birthday", Date))

要するにテーブル

データマッピング(2) マッピング先

class Person(object): """ A person """

def __init__(self, first_name, last_name, birthday): self.first_name, self.last_name, self.birthday = first_name, last_name, birthday

普通のクラス

データマッピング(3) マッピング

person_mapper = mapper(Person, person_table)

Columnがそのままアトリビュートになる

 

ぶっちゃけめんどくさい

宣言的マッピング

Base = declarative_base()

class Person(Base): __tablename__ = 'person' person_id = Column(Integer, primary_key=True) first_name = Column(Unicode(20)), last_name = Column(Unicode(20)), birthday = Column(Date))

クラスとスキーマを同時に定義

関連マッピング

class Employee(Base): __tablename__ = 'employee' id = Column(Integer, primary_key=True) ..... company_id = Column(Integer, ForeignKey('company.id'))

class Company(Base): __tablename__ = 'company' id = Column(Integer, primary_key=True) employees = relation(Employee, backref="company")

多対多

user_keyword_table = Table('user_keyword', meta, Column('user_id', ForeignKey('user.id')), Column('keyword_id', ForeignKey('keyword.id')))

class User(Base): id = Column(Integer, primary_key=True)

class Keyword(Base): id = Column(Integer, primary_key=True) users = relation(User, backref='keywords', secondary=user_keyword_table)

関連属性(1)

class User(Base): id = Column(Integer, primary_key=True) name = Column(String(255), unique=True)

class Keyword(Base): id = Column(Integer, primary_key=True) word = Column(String(255), unique=True)

関連属性(2)

class UserKeywords(Base): user_id = Column(Integer, ForeignKey('user.id')) keyword_id = Column(Integer, ForeignKey('keyword.id')) registered = Column(DateTime) kw= relation(Keyword, backref="users") us = relation(User, backref="keywords")

 

user = User()user.name = 'aodag'keyword = Keyword()keyword.word = 'python'

user_keyword = UserKeyword()user_keyword.registered = datetime.now()user_keyword.us = useruser_keyword.kw= keyword

 

user.kw[0].registereduser.kw[0].kw.wordword.us[0].user.name

2HOPするのがうざい

関連属性 AssociationProxy

class User(Base): id = Column(Integer, primary_key=True) name = Column(String(255), unique=True) keywords = association_proxy('kw', 'keyword')

class Keyword(Base): id = Column(Integer, primary_key=True) word = Column(String(255), unique=True) users= association_proxy('us', 'user')

 

user.kw[0].registereduser.keywords[0].wordkeyword.users[0].name

 

user.kw[0].keyword.word-> user.keyword[0].word

keyword.us[0].user.name-> keyword.users[0].name

継承

RDBの継承実装方法

結合テーブルスーパータイプのテーブルとサブタイプ固有のデータを持つテーブル

単一テーブルすべてのサブタイプのデータを含む1テーブル

完全テーブルサブタイプごとにすべてのデータを持つテーブル

継承(1) スーパークラスの設定

class Person(Base): ... typename = Column(String(20)) __mapper_args__ = {'polymorphic_on':'typename'}

タイプフラグのカラムを追加オプションで、カラムを指定

継承(2) 結合テーブルの場合

class Employee(Person): __tablename__ = 'employee' employee_id = Column(Integer, ForeignKey('person.person_id')) __mapper_args__ = {'polymorphic_identity':'employee'}

サブタイプはテーブルを持つスーパータイプのテーブルを参照する外部参照制約タイプフラグの値を指定

継承(2) 単一テーブル継承

class Employee(Person): __mapper_args__ = {'polymorphic_identity':'employee'}

サブタイプはテーブルを持たない

継承(3) 完全テーブル

class Employee(Person): __tablename__ = 'employee' person_id = Column(Integer, primary_key=True) first_name = Column(Unicode(20)), last_name = Column(Unicode(20)), birthday = Column(Date)) __mapper_args__ = {'concrete':True}

サブタイプはテーブルを持つオプションで完全に別テーブルとする指定スーパータイプにはタイプフラグが必要ないカラムを再度定義

Unit of work

データ処理をマーキングして管理一度にデータベースに反映

メモリ上でのトランザクション処理

SessionとDB接続

DB接続情報engine = create_engine('sqlite:///')

クラスファクトリSession = sessionmaker(bind=engine)

セッションオブジェクトsession = Session()

Session

p = Person(u'篤', u'小田切', datetime(1979, 8, 2))session.add(p)p = Person(u'John', u'Doe', datetime(1970, 1, 1))session.add(p)

session.commit()

新規オブジェクトは、セッションに追加する

Session

p = session.query(Person).filter_by(id=1)p.birthday = date.today()

session.commit()

p = session.query(Person).filter_by(id=1)p.delete()session.commit()

sessionから取り出したオブジェクトはそのまま。

scoped_session

スレッドローカルなモノステートオブジェクト

Session = scoped_session(sessionmaker(bind=engine))Session.query(...)

直接グローバルオブジェクトでメソッド呼び出しできる。同一スレッド内であれば、同一セッション。

内部状態のリセットはremoveメソッドで。Session.remove()

マルチDB

MasterSession = session_maker(bind=create_engine('mysql://db1/db')

SlaveSession =session_maker(bind=create_engine('sqlite://db2/db')

p = Person(...)s1 = MasterSession()s1.add()s1.commit()

s2 = SlaveSession()s2.query(Person).filter_by(id=1)

まとめ

恐ろしく柔軟今回紹介したのはSqlAlchemyができることの1/10くらいその他、コネクションプロキシ2フェーズコミット複数テーブルから1クラスにマッピング1テーブルから複数クラスにマッピングクエリからクラスにマッピング関連の実装クラスをdictやsetに変更アトリビュート単位での遅延ローディング垂直分割、水平分割(sharding)の対応などなど

 

足りないのはadminだけ!多分FormAlchemyで作れば、それほど問題ないかと。マイグレーションは、sqlalchemy-migrate