我正在尝试了解 SQLAlchemy 附带的“Discriminator on association”示例,它定义了 HasAddresses 混入,因此每个子类化 HasAddresses 的模型都神奇地获得了一个 addresses
属性,这是一个集合可以添加哪些地址对象。链接是通过一个中间表执行的,所以乍一看这种关系看起来像多对多,我希望能够将多个地址链接到一个客户,以及将多个客户和供应商链接到一个地址。
但是,Address
模型的设置方式使其具有单个 parent
属性,该属性只能引用单个对象。因此,在示例中,地址只能链接到单个客户或供应商。
如何修改该示例,以便 Address 能够反向引用多个父对象?
最佳答案
我们可以修改 sqlalchemy/examples/generic_associations/table_per_association.py 来添加一个命名的 backref 到 Address,然后是一个汇总所有创建的 backref 的@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
关于python - SQLAlchemy 中与多个父项的通用关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10698852/