我们可以修改 sqlalchemy/examples/generic_associations/table_per_association.py 以将一个命名的反向引用添加到地址,然后是一个 @property,它汇总了所有创建的反向引用。
"""table_per_association.py
The HasAddresses mixin will provide a new "address_association" table for
each parent class. The "address" table will be shared
for all parents.
This configuration has the advantage that all Address
rows are in one table, so that the definition of "Address"
can be maintained in one place. The association table
contains the foreign key to Address so that Address
has no dependency on the system.
"""
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy import create_engine, Integer, Column, \
String, ForeignKey, Table
from sqlalchemy.orm import Session, relationship
import itertools
class Base(object):
"""Base class which provides automated table name
and surrogate primary key column.
"""
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
id = Column(Integer, primary_key=True)
Base = declarative_base(cls=Base)
class Address(Base):
"""The Address class.
This represents all address records in a
single table.
"""
street = Column(String)
city = Column(String)
zip = Column(String)
@property
def all_owners(self):
return list(
itertools.chain(
*[
getattr(self, attr)
for attr in [a for a in dir(self) if a.endswith("_parents")]
]
))
def __repr__(self):
return "%s(street=%r, city=%r, zip=%r)" % \
(self.__class__.__name__, self.street,
self.city, self.zip)
class HasAddresses(object):
"""HasAddresses mixin, creates a new address_association
table for each parent.
"""
@declared_attr
def addresses(cls):
address_association = Table(
"%s_addresses" % cls.__tablename__,
cls.metadata,
Column("address_id", ForeignKey("address.id"),
primary_key=True),
Column("%s_id" % cls.__tablename__,
ForeignKey("%s.id" % cls.__tablename__),
primary_key=True),
)
return relationship(Address, secondary=address_association,
backref="%s_parents" % cls.__name__.lower())
class Customer(HasAddresses, Base):
name = Column(String)
class Supplier(HasAddresses, Base):
company_name = Column(String)
engine = create_engine('sqlite://', echo=True)
Base.metadata.create_all(engine)
session = Session(engine)
a1 = Address(
street='123 anywhere street',
city="New York",
zip="10110")
a2 = Address(
street='40 main street',
city="San Francisco",
zip="95732")
session.add_all([
Customer(
name='customer 1',
addresses=[a1, a2]
),
Supplier(
company_name="Ace Hammers",
addresses=[a1]
),
])
session.commit()
for customer in session.query(Customer):
for address in customer.addresses:
print address.all_owners