動的に生成されるテーブル定義をSQLAlchemyを使って読み込む方法

動的に生成されるテーブル定義をSQLAlchemyを使って読み込む方法

タイトルのことをやりたい！と思って調べたのでメモ。
結論から言うと、組み込み関数のtypeを使ってマッピングのオブジェクトを作ってやればOK。
例えばこんな感じで書く。

```python
engine = create_engine(データベースURL, echo=True, encoding='utf-8')
base = declarative_base(engine)

dic = {
    '__tablename__': テーブル名,
    '__table_args__': {'autoload': True}}}
obj = type('MyObject', (base,), dic)
```

ポイントとしては、既存のテーブルからマッピングを作る```autoload = True```を設定すること。



# サンプル
## サンプルコード
```python:sql_sample.py
# -*- coding: utf-8 -*-
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

schema = 'sqlite:///test.db'

engine = create_engine(schema, echo=False, encoding='utf-8')
base = declarative_base(engine)
session = sessionmaker(bind=engine)()

management_dic = {
    '__tablename__': 'management',
    '__table_args__': {'autoload': True}}
management_object = type('management_object', (base,), management_dic)
fetch_management = session.query(management_object).all()

for management_row_object in fetch_management:
    folder_table_dic = {
        '__tablename__': management_row_object.folder_table_name,
        '__table_args__': {'autoload': True}}
    folder_object_name = management_row_object.folder_table_name.encode('utf-8')
    folder_object = type(folder_object_name, (base,), folder_table_dic)
    fetch_folder = session.query(folder_object).all()

    print "-" * 32
    print "table_name: %s" % folder_object_name
    print "-" * 32
    for row in fetch_folder:
        print "  fid: %s,  file_name: %s" % (row.fid, row.file_name)
    print
```


## サンプルコードを使うためのデータベース設定
### 動的に作成するテーブルを管理するテーブル（management）
tid|folder_table_id
---- | ----
0 | folder_0000
1 | folder_0001

```sql:management.sql
CREATE TABLE management(
    tid INTEGER NOT NULL PRIMARY KEY,
    folder_table_name TEXT NOT NULL);
INSERT INTO management(tid, folder_table_name) VALUES (0,'folder_0000');
INSERT INTO management(tid, folder_table_name) VALUES (1,'folder_0001');

```

### 動的に生成されたテーブル(folder_0000)
fid | file_name
---- | ---- 
0 | ham.txt
1 | spam.txt
2 | egg.txt

```sql:folder_0000.sql
CREATE TABLE folder_0000(
    fid INTEGER NOT NULL PRIMARY KEY,
    file_name TEXT NOT NULL
);
INSERT INTO folder_0000(fid, file_name) VALUES (0,'ham.txt');
INSERT INTO folder_0000(fid, file_name) VALUES (1,'spam.txt');
INSERT INTO folder_0000(fid, file_name) VALUES (2,'egg.txt');

```

### 動的に生成されたテーブル(folder_0001)
fid | file_name 
---- | ---- 
0 | foo.txt
1 | bar.txt

```sql:folder_0001.sql
CREATE TABLE folder_0001(
    fid INTEGER NOT NULL PRIMARY KEY,
    file_name TEXT NOT NULL
);
INSERT INTO folder_0001(fid, file_name) VALUES (0,'foo.txt');
INSERT INTO folder_0001(fid, file_name) VALUES (1,'bar.txt');
```

## サンプルの実行
```bash
sqlite3 test.db < management.sql
sqlite3 test.db < folder_0000.sql
sqlite3 test.db < folder_0001.sql
python sql_sample.py
```

## サンプルの実行結果
```
--------------------------------
table_name: folder_0000
--------------------------------
  fid: 0,  file_name: ham.txt
  fid: 1,  file_name: spam.txt
  fid: 2,  file_name: egg.txt

--------------------------------
table_name: folder_0001
--------------------------------
  fid: 0,  file_name: foo.txt
  fid: 1,  file_name: bar.txt

```

# 
# 参考
[Python > Documentation > Python標準ライブラリ > 2.組み込み関数](http://docs.python.jp/2/library/functions.html#type)
[Dynamically setting \__tablename__ for sharding in SQLAlchemy?](http://stackoverflow.com/questions/19163911/dynamically-setting-tablename-for-sharding-in-sqlalchemy)