SQLAlchemy 入門
ORM編
for Kobe Python Meetup #14
2017/10/20 Kobe Japan
Yasushi Masuda PhD
( @whosaysni )
Tech team, Core IT grp. IT Dept.
MonotaRO Co., LTD.
Pythonista since 2001 (2.0~)
• elaphe (barcode library)
• oikami.py (老神.py)
• PyCon JP founder
翻訳もろもろ
参考文献
オンラインドキュメント:

http://docs.sqlalchemy.org/en/rel_1_1/



(古いけど)和訳:

http://omake.accense.com/static/doc-ja/
sqlalchemy/
準備
sakila DB SQLite版
https://github.com/jOOQ/jOOQ/jOOQ-examples/Sakila/sqlite-sakila-db/
Sakila
• MySQL エンジニア
作のサンプルDB
• レンタルビデオ屋

(...通じます?) のモ
デル
• BSD ライセンス
スキーマのドキュメントは下記
https://dev.mysql.com/doc/sakila/en/sakila-structure-tables.html
(上記のサイトのSQLiteデータベースバイナリは壊れているので、以下から取得してください)
https://github.com/wallymathieu/sakila-sample-database-ports/blob/master/sqlite-sakila-db/sqlite-sakila.sq
アジェンダ
前回のあらすじ
automapでORMを体験しよう



おまけ:そのほかのマッピング方法
前回のあらすじ
SQLAlchemyを3行で
エンジンを使ってみましょう



SQL式を使ってみましょう
ORMを使ってみましょう
#	クエリはエンジンで実行する

engine	=	create_engine(engine_url)

engine.execute('select	...	from	...')

#	SQLを表すクエリオブジェクトを構築できる

film	=	table('film',	column('film_id'),	...)

q	=	select([film]).where(film.c.film_id==1)

engine.execute(q)

#	スキーマ定義を使うとDDLも生成・実行できる

film	=	Table('Film',	MetaData(),	Column('film_id',	INTEGER),	...)

f.create(bind=engine)

engine.execute(select([film.c.film_id,	...])).fetchone()

#	宣言的ORMはクラスでテーブル構造を表現する

Base	=	declarative_base()

class	Film(Base):

				film_id	=	Column(INTEGER,	primary_key=True)

				...

#	ORMでのレコードの操作にはセッションを使う

Session	=	sessionmaker(bind=engine)

session	=	Session()

session.query(Film).get(1)

f	=	session.query(Film).filter(Film.film_id>3).first()

f.rating	=	'PG-18'	#	カラム値の変更

session.flush()	#	保存
ORM
ORMでできたら嬉しいこと再確認
• DBレコードをオブジェクトのように扱いたい
• 1レコード1オブジェクト
• 別テーブルのレコード参照→別クラスのオブジェクトの参照
• オブジェクトのデータをDBにシリアライズしたい
• インスタンスを保存・あとで取り出したい
• DBの機能を使ってオブジェクトを操作したい
• オブジェクトの検索・フィルタリング
SQLAlchemyのORM
• 1つのテーブルを1つのクラスにマッピングする
• テーブルのレコードがクラスのインスタンス
• 別テーブルのレコード(のオブジェクト)は、マッピングクラスにリレー
ション (relation) を定義して参照できる
• セッションを使ってオブジェクトデータをDBから読み出し・保存できる
• セッションのフラッシュ(精算)で、オブジェクトとDBの状態とが一致する
• セッションのコミット(承認)で、変更内容がDBに永続化される
• オブジェクトを取り出すときに SQL式 で検索条件を設定できる
ORM操作の流れ
マッピングを設定する
セッションクラスを設定する
セッションインスタンスを作る
オブジェクトを

DBから取り出す
オブジェクトを
クラスから生成する
セッションに

追加する
オブジェクトを
更新する
セッションをflush/rollback
マッピング
関連の操作
セッションの

操作
セッションを破棄
毎回セッションを
作る時のサイクル
セッションを
使い続けるときの
サイクル
classic(古典的) mapping

古いやつ昔からあるやつ

新しいやつを支えている

古の魔法 = よくわからんから避けられる

declarative(宣言的) mapping

新しいやつ 書きやすい 流行の宣言的API

古の魔法x現代の魔法=よくわからないけど便利

SAでORMといえばだいたいこっちの話

automapping

宣言的マッピングをある程度自動化してくれる

ある意味最強の呪文
SQLAlchemyのマッピング
古典的マッピング
Tableオブジェクト
Column foo
Column bar
Column baz
...
エンティティクラス
マッピングクラス
仕掛けの付いた foo
仕掛けの付いた bar
仕掛けの付いた baz
...
method qux
method quux
method qux
method quux
...
その他有象無象
...
その他有象無象
mapperの仕掛けた

内部オブジェクト


mapper
宣言ベースクラス
マッピングクラスの
定義
宣言的マッピング
Column foo
Column bar
Column baz
...
マッピングクラス
仕掛けの付いた foo
仕掛けの付いた bar
仕掛けの付いた baz
...
method qux
method quux
method qux
method quux
...
その他有象無象
...
その他有象無象
内部オブジェクト
Tableオブジェクト
宣言ベースクラスの機能
Column foo
Column bar
Column baz
mapping
自動マッピング

ベースクラス
マッピングクラスの
定義
自動マッピング
マッピングクラス
仕掛けの付いた foo
仕掛けの付いた bar
仕掛けの付いた baz
...
method qux
method quux
method qux
method quux
...
その他有象無象
...
その他有象無象
得体の知れない

内部オブジェクト
Tableオブジェクト
ベースクラス機能
Column foo
Column bar
Column baz
(スキーマ定義)
DB上のスキーマ構造
mapping
セッション
• セッション:トランザクションのようなもの

エンジンのDB接続一つが対応している

通常、セッションの持続中は、他のプログラムは

セッションが捕まえている接続にアクセスできない
• オブジェクトの読み出し:SELECT

新たなオブジェクトをセッションに追加:INSERT

オブジェクトのアトリビュートを更新:UPDATE

セッションにオブジェクトをdeleteさせる:DELETE
• 必要に応じてクエリを実行し、DB(のトランザクション)と

状態を同期する
• 通常は、セッションをcommit()するとDBを更新する

(トランザクションをCOMMITする)
セッション
Program
query A SELECT A
record Aobject A
start

tracking A
...
(updates) flag A as
"dirty"
reflect new/dirty
changes
flush()
start

tracking B
...
add(b)
UPDATE A
INSERT B
COMMIT
BEGIN
(create) Database
(new object)
commit()
使ってみましょう
• automapを使って楽してマッピングを設定しましょう

(declarativeやclassic mappingは後で)

• セッションを作成して、マッピングを使ってみましょう
ORM操作の流れ
マッピングを設定する
セッションクラスを設定する
セッションインスタンスを作る
オブジェクトを

DBから取り出す
オブジェクトを
クラスから生成する
セッションに

追加する
オブジェクトを
更新する
セッションをflush/commit/rollback
マッピング
関連の操作
セッションの

操作
セッションを破棄
毎回セッションを
作る時のサイクル
セッションを
使い続けるときの
サイクル
マッピングベースクラスを作る

↓

エンジンを指定してマッピングを準備する

(DBからスキーマを呼び出してマッピングする)

automappingの流れ
#	SQLエコーバックモードを有効にして接続する

>>>	from	sqlalchemy	import	create_engine

>>>	e	=	create_engine('sqlite:///sqlite-sakila.sq',	echo=True)

#	filmテーブルがあるか確かめる

>>>	e.execute('select	count(*)	from	film').scalar()

2017-10-01	07:27:45,014	INFO	sqlalchemy.engine.base.Engine	
select	count(*)	from	film

2017-10-01	07:27:45,014	INFO	sqlalchemy.engine.base.Engine	()

1002

>>>

automappingを使ってみましょう

(エンジンの生成)
↑SQLAlchemyのエコーバック
#	自動マッピングベースクラスを生成

>>>	from	sqlalchemy.ext.automap	import	automap_base

>>>	Base	=	automap_base()

#	エンジンを指定してテーブル情報を反映させる

>>>	Base.prepare(engine=e,	reflect=True)

2017-10-01	07:37:18,014	INFO	sqlalchemy.engine.base.Engine	SELECT	CAST	....

...

2017-10-01	07:37:18,019	INFO	sqlalchemy.engine.base.Engine	SELECT	name	FROM	sqlite_master	WHERE	type='table'	ORDER	BY	name

2017-10-01	07:37:18,019	INFO	sqlalchemy.engine.base.Engine	()

2017-10-01	07:37:18,021	INFO	sqlalchemy.engine.base.Engine	PRAGMA	table_info("actor")

2017-10-01	07:37:18,022	INFO	sqlalchemy.engine.base.Engine	()

2017-10-01	07:37:18,023	INFO	sqlalchemy.engine.base.Engine	SELECT	sql	FROM		(SELECT	*	FROM	sqlite_master	UNION	ALL			
SELECT	*	FROM	sqlite_temp_master)	WHERE	name	=	'actor'	AND	type	=	'table'

2017-10-01	07:37:18,023	INFO	sqlalchemy.engine.base.Engine	()

2017-10-01	07:37:18,024	INFO	sqlalchemy.engine.base.Engine	PRAGMA	foreign_key_list("actor")

2017-10-01	07:37:18,024	INFO	sqlalchemy.engine.base.Engine	()

2017-10-01	07:37:18,024	INFO	sqlalchemy.engine.base.Engine	SELECT	sql	FROM		(SELECT	*	FROM	sqlite_master	UNION	ALL			
SELECT	*	FROM	sqlite_temp_master)	WHERE	name	=	'actor'	AND	type	=	'table'

2017-10-01	07:37:18,024	INFO	sqlalchemy.engine.base.Engine	()

2017-10-01	07:37:18,026	INFO	sqlalchemy.engine.base.Engine	PRAGMA	index_list("actor")

2017-10-01	07:37:18,026	INFO	sqlalchemy.engine.base.Engine	()

...

>>>	list(Base.classes)		#	テーブルが読み込めている

[<class	'sqlalchemy.ext.automap.category'>,	<class	'sqlalchemy.ext.automap.city'>,	<class	'sqlalchemy.ext.automap.store'>,	
<class	'sqlalchemy.ext.automap.film_text'>,	<class	'sqlalchemy.ext.automap.language'>,	<class	
'sqlalchemy.ext.automap.country'>,	<class	'sqlalchemy.ext.automap.actor'>,	<class	'sqlalchemy.ext.automap.film_category'>,	
<class	'sqlalchemy.ext.automap.customer'>,	<class	'sqlalchemy.ext.automap.film_actor'>,	<class	
'sqlalchemy.ext.automap.inventory'>,	<class	'sqlalchemy.ext.automap.address'>,	<class	'sqlalchemy.ext.automap.staff'>,	
<class	'sqlalchemy.ext.automap.rental'>,	<class	'sqlalchemy.ext.automap.payment'>,	<class	'sqlalchemy.ext.automap.film'>,	
<class	'sqlalchemy.ext.automap.location'>]

>>>
automappingを使ってみましょう

(ベースクラスの準備)
↓テーブル一覧を取得
テーブルの情報を取得
#	Base.classes	は(自動的に作られた)テーブル名でアクセスできる

>>>	Film	=	Base.classes.film

>>>	Film

<class	'sqlalchemy.ext.automap.film'>

#	Filmクラスはマッピング済み

>>>	dir(Film)

['__abstract__',	'__class__',	...	,	'description',	'film_id',	'language',	
'language_id',	'last_update',	'length',	'metadata',	
'original_language_id',	'prepare',	'rating',	'release_year',	
'rental_duration',	'rental_rate',	'replacement_cost',	'special_features',	
'title']

#	スキーマ定義もできている

>>>	Film.__table__

Table('film',	MetaData(bind=None),

Column('film_id',	INTEGER(),	table=<film>,	primary_key=True,	...),

Column('title',	VARCHAR(length=255),	table=<film>,	nullable=False),	...)

>>>
automappingを使ってみましょう

(マッピングクラスを参照する)
ORM操作の流れ
マッピングを設定する
セッションクラスを設定する
セッションインスタンスを作る
オブジェクトを

DBから取り出す
オブジェクトを
クラスから生成する
セッションに

追加する
オブジェクトを
更新する
セッションをflush/commit/rollback
マッピング
関連の操作
セッションの

操作
セッションを破棄
毎回セッションを
作る時のサイクル
セッションを
使い続けるときの
サイクル
セッションを作ってみましょう

(セッションの生成)
#	セッションを作るクラスを	sessionmaker	で作る

#	使うエンジンが一定ならこのとき指定する

>>>	from	sqlalchemy.orm	import	sessionmaker

>>>	Session	=	sessionmaker(bind=e)

>>>	session	=	Session()

>>>	session

<sqlalchemy.orm.session.Session	object	at	...>

#	dir(session)	してみましょう
ORM操作の流れ
マッピングを設定する
セッションクラスを設定する
セッションインスタンスを作る
オブジェクトを

DBから取り出す
オブジェクトを
クラスから生成する
セッションに

追加する
オブジェクトを
更新する
セッションをflush/commit/rollback
マッピング
関連の操作
セッションの

操作
セッションを破棄
毎回セッションを
作る時のサイクル
セッションを
使い続けるときの
サイクル
DBからオブジェクトを取り出しましょう

(クエリを生成する)
#	session.query()	は	Model	クラスを問い合わせるための

#	クエリオブジェクトを返す

>>>	Film	=	Base.classes.film

>>>	q	=	session.query(Film)

>>>	q

<sqlalchemy.orm.query.Query	object	at	...>

#	str(q)	するとクエリを返す(実行はされない)

>>>	str(q)

'SELECT	film.film_id	AS	film_film_id,	...	FROM	film'
DBからオブジェクトを取り出しましょう

(クエリを実行する)
#	実際にオブジェクトを取り出す操作を実行したときに

#	クエリが実行される

>>>	q.get(1)		#	プライマリキーが	1

2017-10-01	23:29:52,678	INFO	sqlalchemy.engine.base.Engine	SELECT	
film.film_id	AS	film_film_id,	...

FROM	film

WHERE	film.film_id	=	?

...

<sqlalchemy.ext.automap.film	object	at	0x1013b2810>

>>>	q.first()		#	条件に一致する最初のレコード

2017-10-01	23:29:52,678	INFO	sqlalchemy.engine.base.Engine	SELECT	...

FROM	film

	LIMIT	?	OFFSET	?

2017-10-19	23:29:52,678	INFO	sqlalchemy.engine.base.Engine	(1,	0)

<sqlalchemy.ext.automap.film	object	at	0x1013bce10>

>>>	q[3]		#	インデクスでアクセスしても取り出せる

...

<sqlalchemy.ext.automap.film	object	at	0x1013d0250>
セッションの中では

同じレコードは

同じオブジェクト
DBからオブジェクトを取り出しましょう

(クエリを実行する)
#	all()	は条件に一致する全てのレコードを返す

>>>	q.all()

...

[<sqlalchemy.ext.automap.film	object	at	0x1013d00d0>,	
<sqlalchemy.ext.automap.film	object	at	0x1013bcf90>,	...]

#	クエリをスライスすると	"LIMIT	...	OFFSET	..."

>>>	q[:10]

2017-10-01	23:36:51,815	INFO	sqlalchemy.engine.base.Engine	SELECT	...

FROM	film

	LIMIT	?	OFFSET	?

...

[<sqlalchemy.ext.automap.film	object	at	0x1013d00d0>,	...]
DBからオブジェクトを取り出しましょう

(クエリ条件を指定する)
#	filter()	に	SQL	式を渡せば絞りこめる

#	filter()	は条件の付加されたクエリオブジェクトを返す

>>>	q2	=	q.filter(Film.language_id==1)

>>>	q2

<sqlalchemy.orm.query.Query	object	at	0x1013d9690>

>>>	str(q2)

'SELECT	film.film_id	...	FROM	film	WHERE	film.language_id	=	?'

#	一致条件は	filter_by	でも指定できる

>>>	q.filter_by(language_id=2).all()

2017-10-01	23:52:27,650	INFO	sqlalchemy.engine.base.Engine	SELECT	...

FROM	film

WHERE	film.language_id	=	?

#	filter,	filter_by	をつなげて条件を積み上げられる

>>>	q.filter(Film.rating=='G').filter(Film.title.like('%MOON%')).all()

2017-10-02	00:05:34,056	INFO	sqlalchemy.engine.base.Engine	SELECT	...

FROM	film

WHERE	film.rating	=	?	AND	film.title	LIKE	?

....

[<sqlalchemy.ext.automap.film	object	at	...>,	...]
オブジェクトの値にアクセスしましょう

(オブジェクトの中身)
>>>	f	=	q.first

#	オブジェクトにはテーブルのカラムと同じ名前のアトリビュート
がある

#	language_id	だけでなく	lanugage	というアトリビュートがある

#	_collection	のついた怪しげなアトリビュートもある

>>>	dir(f)

[...,	'description',	'film_actor_collection',	
'film_category_collection',	'film_id',	
'inventory_collection',	'language',	'language_id',	
'last_update',	'length',	'metadata',	'original_language_id',	
'prepare',	'rating',	'release_year',	'rental_duration',	
'rental_rate',	'replacement_cost',	'special_features',	
'title']
オブジェクトの値にアクセスしましょう
(カラム値にアクセスする)
#	カラム名のアトリビュートから値にアクセスできる

>>>	f.title

'ACADEMY	DINOSAUR'

#	カラムのデータ型に応じて適切なPythonデータ型になる

>>>	[f.title,	f.length,	f.rental_rate,	f.last_update]

['ACADEMY	DINOSAUR',	86,	Decimal('0.99'),	
datetime.datetime(2011,	9,	14,	18,	5,	32)]
inventory_collection
オブジェクトの値にアクセスしましょう
(外部キー参照にアクセスする)
#	language	は自動マッピングで作られた	Language	への参照

#	参照すると自動的にクエリを実行する

>>>	f.language.name

2017-10-02	00:22:06,996	INFO	sqlalchemy.engine.base.Engine	SELECT	
language.language_id	...

FROM	language

WHERE	language.language_id	=	?

...

'English'

#	*_collection	は他のテーブルからの参照の逆参照で、リストを返す

>>>	f.inventory_collection

2017-10-02	00:11:48,828	INFO	sqlalchemy.engine.base.Engine	SELECT	
inventory.inventory_id	...

FROM	inventory

WHERE	?	=	inventory.film_id

...

[<sqlalchemy.ext.automap.inventory	object	at	0x101510850>,	...]

film language
film
film
inventory
inventory
backref
ORM操作の流れ
マッピングを設定する
セッションクラスを設定する
セッションインスタンスを作る
オブジェクトを

DBから取り出す
オブジェクトを
クラスから生成する
セッションに

追加する
オブジェクトを
更新する
セッションをflush/rollback
マッピング
関連の操作
セッションの

操作
セッションを破棄
毎回セッションを
作る時のサイクル
セッションを
使い続けるときの
サイクル
オブジェクトの値を更新しましょう
(アトリビュートの更新)
#	アトリビュートに代入すると、値をセットできる

#	まだ	SQL	は実行されない

>>>	f.title	=	'HOLY	GRAIL'

>>>	f.title

'HOLY	GRAIL'

#	次のDB操作前にUPDATEが実行される(トランザクション内)

>>>	q.count()

2017-10-02	00:48:07,739	INFO	sqlalchemy.engine.base.Engine	UPDATE	film	SET	title=?	WHERE	film.film_id	=	?

#	セッションを	rollback()	すると更新はすべて破棄される

#	ロールバックしたオブジェクトを再度参照するとDBクエリが実行される

>>>	s.rollback()

>>>	f.title

2017-10-02	00:51:24,693	INFO	sqlalchemy.engine.base.Engine	SELECT	film.film_id	...

'ACADEMY	DINOSAUR'

#	セッションを	flush()	すると(トランザクション内で)更新SQLが走る

>>>	f.title	=	'HOLY	GRAIL'

>>>	s.flush()

2017-10-02	06:20:56,690	INFO	sqlalchemy.engine.base.Engine	UPDATE	film	SET	title=?	WHERE	film.film_id	=	?

...

#	セッションを	commit()	するとトランザクションを	COMMIT	する

>>>	s.commit()

2017-10-02	06:29:34,104	INFO	sqlalchemy.engine.base.Engine	COMMIT
オブジェクトの値を更新しましょう
(リレーションの更新)
#	リレーションのアトリビュートに代入すると、値をセットできる

#	代入するのはマッピングクラスのインスタンス

>>>	Language	=	Base.classes.language

>>>	lang	=	session.query(Language).get(2)

>>>	lang.id,	lang.name

(2,	'Italian')

>>>	f.language_id,	f.language.language_id,	f.name

(1,	1,	'English')

>>>	f.lanugage	=	lang

#	注意:	language_id	は変更されない!

#	flush()	してはじめて外部キーが更新される

>>>	f.language_id,	f.language.language_id,	f.name

(1,	2,	'Italian')

>>>	session.flush()

>>>	f.language_id,	f.language.language_id,	f.name

(2,	2,	'Italian')

####	大事なこと:リレーションを更新したら必ず	flush()	する
ORM操作の流れ
マッピングを設定する
セッションクラスを設定する
セッションインスタンスを作る
オブジェクトを

DBから取り出す
オブジェクトを
クラスから生成する
セッションに

追加する
オブジェクトを
更新する
セッションをflush/rollback
マッピング
関連の操作
セッションの

操作
セッションを破棄
毎回セッションを
作る時のサイクル
セッションを
使い続けるときの
サイクル
オブジェクトを新規追加しましょう
#	カラムの値をキーワードパラメタで渡してオブジェクトを生成する

>>>	Language(language_id=99,	name='Japanese',	last_update=datetime.now())

<sqlalchemy.ext.automap.language	object	at	0x103243f60>

>>>	l	=	Language(language_id=99,	name='Japanese',	last_update=datetime.now())

#	セッションに追加すると新規追加対象になる

>>>	s.add(l)

#	セッションを	flush()	すると	INSERT	文が実行される

>>>	s.flush()

2017-10-02	07:12:26,651	INFO	sqlalchemy.engine.base.Engine	BEGIN	(implicit)

2017-10-02	07:12:26,652	INFO	sqlalchemy.engine.base.Engine	INSERT	INTO	
language	(language_id,	name,	last_update)	VALUES	(?,	?,	?)

2017-10-02	07:12:26,652	INFO	sqlalchemy.engine.base.Engine	(99,	'Japanese',	
'2017-10-02	07:12:12.932744')

>>>
オブジェクトを削除しましょう
#	セッションの	delete()	メソッドに削除したいレコード(のオブジェクト)を渡す

>>>	s.delete(l)

>>>	s.flush()

2017-10-02	07:19:53,458	INFO	sqlalchemy.engine.base.Engine	SELECT	...

FROM	film

WHERE	?	=	film.original_language_id

2017-10-02	07:19:53,464	INFO	sqlalchemy.engine.base.Engine	DELETE	FROM	
language	WHERE	language.language_id	=	?

2017-10-02	07:19:53,464	INFO	sqlalchemy.engine.base.Engine	(99,)
まとめ
• automapを使ってマッピングをDBから生成できる
• session.query() でクエリを生成して

DBからオブジェクトを取り出す
• 新しくオブジェクトを追加するときはセッションにadd()

削除するときは delete()
• flush()するか、次にDBと同期する必要が出るまでクエリは実行
されない
• セッションを commit() すると、トランザクションが反映される
#	ベースクラスを生成する

from	sqlalchemy.ext.declarative	import	declarative_base

Base	=	declarative_base()

#	ベースクラスを拡張するとORMで扱えるモデルクラスになる

from	sqlalchemy	import	Column,	Integer,	String,	DateTime



class	Actor(Base):

				__tablename__	=	'actor'

				actor_id	=	Column(Integer,primary_key=True)

				first_name	=	Column(String)

				last_name	=	Column(String)

				last_update	=	Column(DateTime)

				def	full_name(self):

								return	'{}	{}'.format(

								self.first_name,	self.last_name)

宣言的マッピング
#	テーブルのスキーマ定義を作成する

>>>	from	sqlalchemy	import	Table,	Column,	MetaData,	Integer

>>>	foo_table	=	Table(

...					'foo',	MetaData(),	Column('bar',	Integer))

#	エンティティクラスを作成する

>>>	class	Foo(object):

...					def	bar_is_odd(self):

...									return	bool(bar	%	2)

#	マップする

>>>	from	sqlalchemy.orm	import	mapper

>>>	mapper(Foo,	foo_table)

<Mapper	at	0x105e332d0;	Foo>

#	dir(Foo)	してみましょう。	Foo.bar	があることを確認しましょう。

#	マッピングに失敗して、やりなおしたら以下のようなメッセージが出る場合:

sqlalchemy.exc.ArgumentError:	Class	'...'	already	has	a	primary	mapper	defined.	
Use	non_primary=True	to	create	a	non	primary	Mapper.		clear_mappers()	will	
remove	*all*	current	mappers	from	all	classes.

>>>	from	sqlalchemy.orm	import	clear_mappers

>>>	clear_mappers()
古典的マッピング
以上です。
おつかれさまでした!

PlaySQLAlchemyORM2017.key