2
"polymorphic" associations, ala SQLAlchemy.
4
This example generalizes the function in poly_assoc_pk.py into a function
5
"association" which creates a new polymorphic association "interface".
8
from sqlalchemy import MetaData, Table, Column, Integer, String, \
10
from sqlalchemy.orm import mapper, relationship, sessionmaker, \
13
metadata = MetaData('sqlite://')
15
def association(cls, table):
16
"""create an association 'interface'."""
18
interface_name = table.name
19
attr_name = "%s_rel" % interface_name
21
metadata = table.metadata
22
association_table = Table("%s_associations" % interface_name, metadata,
23
Column('assoc_id', Integer, primary_key=True),
24
Column('type', String(50), nullable=False)
27
class GenericAssoc(object):
28
def __init__(self, name):
31
def interface(cls, name, uselist=True):
33
mapper = class_mapper(cls)
34
table = mapper.local_table
35
mapper.add_property(attr_name,
36
relationship(GenericAssoc,
37
backref='_backref_%s' % table.name)
41
# list based property decorator
43
if getattr(self, attr_name) is None:
44
setattr(self, attr_name, GenericAssoc(table.name))
45
return getattr(self, attr_name).targets
46
setattr(cls, name, property(get))
48
# scalar based property decorator
50
return getattr(self, attr_name).targets[0]
52
if getattr(self, attr_name) is None:
53
setattr(self, attr_name, GenericAssoc(table.name))
54
getattr(self, attr_name).targets = [value]
55
setattr(cls, name, property(get, set))
59
return getattr(self.association,
60
'_backref_%s' % self.association.type)
62
setattr(cls, 'member', member)
64
mapper(GenericAssoc, association_table, properties={
65
'targets':relationship(cls, backref='association'),
74
addresses = Table("addresses", metadata,
75
Column('id', Integer, primary_key=True),
76
Column('assoc_id', Integer, ForeignKey('addresses_associations.assoc_id')),
77
Column('street', String(100)),
78
Column('city', String(50)),
79
Column('country', String(50))
82
class Address(object):
85
# create "addressable" association
86
addressable = association(Address, addresses)
88
mapper(Address, addresses)
94
users = Table("users", metadata,
95
Column('id', Integer, primary_key=True),
96
Column('name', String(50), nullable=False),
97
Column('assoc_id', Integer, ForeignKey('addresses_associations.assoc_id'))
105
# use the association
106
addressable(User, 'addresses', uselist=True)
111
orders = Table("orders", metadata,
112
Column('id', Integer, primary_key=True),
113
Column('description', String(50), nullable=False),
114
Column('assoc_id', Integer, ForeignKey('addresses_associations.assoc_id'))
120
mapper(Order, orders)
121
addressable(Order, 'address', uselist=False)
125
metadata.create_all()
131
o1.description = 'order 1'
134
u1.addresses.append(a1)
135
a1.street = '123 anywhere street'
138
u1.addresses.append(a2)
139
a2.street = '345 orchard ave'
141
o1.address = Address()
142
o1.address.street = '444 park ave.'
144
sess = sessionmaker()()
149
# query objects, get their addresses
151
bob = sess.query(User).filter_by(name='bob').one()
152
assert [s.street for s in bob.addresses] == \
153
['123 anywhere street', '345 orchard ave']
155
order = sess.query(Order).filter_by(description='order 1').one()
156
assert order.address.street == '444 park ave.'
158
# query from Address to members
160
for address in sess.query(Address).all():
161
print "Street", address.street, "Member", address.member