3
3
The HasAddresses mixin will provide a relationship
4
4
to the fixed Address table based on a fixed association table.
6
The association table will also contain a "discriminator"
6
The association table contains a "discriminator"
7
7
which determines what type of parent object associates to the
8
Address row. SQLAlchemy's single-table-inheritance feature is used
9
to target different association types.
10
11
This is a "polymorphic association". Even though a "discriminator"
11
12
that refers to a particular table is present, the extra association
12
13
table is used so that traditional foreign key constraints may be used.
14
This configuration has the advantage that a fixed set of tables
15
are used, with no extra-table-per-parent needed. The individual
16
Address record can also locate its parent with no need to scan
15
This configuration attempts to simulate a so-called "generic foreign key"
16
as closely as possible without actually foregoing the use of real
17
foreign keys. Unlike table-per-related and table-per-association,
18
it uses a fixed number of tables to serve any number of potential parent
19
objects, but is also slightly more complex.
20
from sqlalchemy.ext.declarative import declarative_base, declared_attr
22
from sqlalchemy.ext.declarative import as_declarative, declared_attr
21
23
from sqlalchemy import create_engine, Integer, Column, \
22
String, ForeignKey, Table
23
25
from sqlalchemy.orm import Session, relationship, backref
24
26
from sqlalchemy.ext.associationproxy import association_proxy
26
29
class Base(object):
27
30
"""Base class which provides automated table name
28
31
and surrogate primary key column.
32
35
def __tablename__(cls):
33
36
return cls.__name__.lower()
34
37
id = Column(Integer, primary_key=True)
35
Base = declarative_base(cls=Base)
37
39
class AddressAssociation(Base):
38
40
"""Associates a collection of Address objects
42
44
__tablename__ = "address_association"
45
def creator(cls, discriminator):
46
"""Provide a 'creator' function to use with
47
the association proxy."""
49
return lambda addresses:AddressAssociation(
51
discriminator=discriminator)
53
46
discriminator = Column(String)
54
47
"""Refers to the type of parent."""
58
"""Return the parent object."""
59
return getattr(self, "%s_parent" % self.discriminator)
49
__mapper_args__ = {"polymorphic_on": discriminator}
61
51
class Address(Base):
62
52
"""The Address class.
68
association_id = Column(Integer,
69
ForeignKey("address_association.id")
58
association_id = Column(Integer, ForeignKey("address_association.id"))
71
59
street = Column(String)
72
60
city = Column(String)
73
61
zip = Column(String)
74
association = relationship(
62
association = relationship("AddressAssociation", backref="addresses")
78
64
parent = association_proxy("association", "parent")
91
77
def address_association_id(cls):
92
return Column(Integer,
93
ForeignKey("address_association.id"))
78
return Column(Integer, ForeignKey("address_association.id"))
96
81
def address_association(cls):
97
discriminator = cls.__name__.lower()
98
cls.addresses= association_proxy(
83
discriminator = name.lower()
86
"%sAddressAssociation" % name,
87
(AddressAssociation, ),
90
"polymorphic_identity": discriminator
95
cls.addresses = association_proxy(
99
96
"address_association", "addresses",
100
creator=AddressAssociation.creator(discriminator)
97
creator=lambda addresses: assoc_cls(addresses=addresses)
102
return relationship("AddressAssociation",
103
backref=backref("%s_parent" % discriminator,
99
return relationship(assoc_cls,
100
backref=backref("parent", uselist=False))
107
103
class Customer(HasAddresses, Base):