SQLAlchemy Many to Many

UsageRecipes/ManyToManySelect - sqlalchemy - Trac
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/ManyToManySelect

おそらく0.1対応なので0.2に書き直してみました。
英語のコメントは省略してあります。

二つ通りのパターンでマッピングしてますが、結局同じです。
個人的には前者の1個にまとめてしまうパターンが好みです。



from sqlalchemy import *

engine = create_engine('sqlite:///')
engine.echo = False
engine.echo_uow = False

metadata = BoundMetaData(engine)

info_table = Table('infos', metadata,
    Column('pk', Integer, primary_key=True),
    Column('info', String))

data_table = Table('data', metadata,
    Column('pk', Integer, primary_key=True),
    Column('data_val', String))

rel_table = Table('rels', metadata,
    Column('info_pk', Integer, ForeignKey(info_table.c.pk)),
    Column('data_pk', Integer, ForeignKey(data_table.c.pk)))

metadata.create_all()

info_table.insert().execute(
    {'pk':1, 'info':'pk_1_info'},
    {'pk':2, 'info':'pk_2_info'},
    {'pk':3, 'info':'pk_3_info'},
    {'pk':4, 'info':'pk_4_info'},
    {'pk':5, 'info':'pk_5_info'})

rel_table.insert().execute(
    {'info_pk':1, 'data_pk':10},
    {'info_pk':1, 'data_pk':11},
    {'info_pk':1, 'data_pk':12},
    {'info_pk':1, 'data_pk':13},
    {'info_pk':2, 'data_pk':20},
    {'info_pk':2, 'data_pk':21},
    {'info_pk':2, 'data_pk':22},
    {'info_pk':2, 'data_pk':23},
    {'info_pk':3, 'data_pk':30},
    {'info_pk':3, 'data_pk':31},
    {'info_pk':3, 'data_pk':32},
    {'info_pk':3, 'data_pk':33})

data_table.insert().execute(
    {'pk':10, 'data_val':'10_data'},
    {'pk':11, 'data_val':'11_data'},
    {'pk':12, 'data_val':'12_data'},
    {'pk':13, 'data_val':'13_data'},
    {'pk':20, 'data_val':'20_data'},
    {'pk':21, 'data_val':'21_data'},
    {'pk':22, 'data_val':'22_data'},
    {'pk':23, 'data_val':'23_data'},
    {'pk':30, 'data_val':'30_data'},
    {'pk':31, 'data_val':'31_data'},
    {'pk':32, 'data_val':'32_data'},
    {'pk':33, 'data_val':'33_data'})

sql_result = engine.text("""
   SELECT infos.pk, data.data_val
   FROM infos JOIN rels ON infos.pk = rels.info_pk
              JOIN data ON rels.data_pk = data.pk;
         """).execute().fetchall()

print 'The result from the SQL Query:\n' + repr(sql_result)
print

sas_result = select([info_table.c.pk, data_table.c.data_val],
   from_obj=[info_table.join(rel_table, info_table.c.pk == rel_table.c.info_pk).join(
    data_table, rel_table.c.data_pk == data_table.c.pk)]).execute().fetchall()

print 'The result from the SA SELECT Tokenized query:\n' + repr(sas_result)
assert repr(sql_result) == repr(sas_result)
print

class Information(object):
    pass

class Data(object):
    pass

session = create_session()

#makeshift->
mapper(Data, data_table)
mapper(Information, info_table, properties=dict(
    datas=relation(Data, secondary=rel_table, lazy=False)))
clear_mappers()
#<-makeshift

Information.mapper = mapper(Information, info_table)
Data.mapper = mapper(Data, data_table)

Information.mapper.add_property('datas', relation(Data.mapper,
    rel_table,
    primaryjoin=info_table.c.pk==rel_table.c.info_pk,
    secondaryjoin=rel_table.c.data_pk==data_table.c.pk))

#Information.mapper == Information
for i in session.query(Information.mapper).select():
    print "Examining: ", i.pk, i.info
    for d in i.datas:
        print '\t', d.pk, d.data_val

長いなぁ…。