python - SQLAlchemy 中与多个父项的通用关联

标签 python sqlalchemy

我正在尝试了解 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/

相关文章:

python - Eve SQLAlchemy 查询串联

SQLAlchemy 与 Python 3.2 : C extensions are not supported?

sqlalchemy - Flask-SQLAlchemy 不会更新或删除一行

python - 如何从文本文件中的每一行创建一个字典条目?

python - 如何通过月份名称查询sqlalchemy

python - 堆栈并评估后缀

python - flask_sqlalchemy create_all 无需导入模型

python - 考虑到 OpenCV 在使用 WarpAffine 旋转时会向图像添加黑色边框,如何旋转坐标?

python - odoo中通过点击事件弹出已有view的窗口

python - 将 sqlalchemy 库添加到 pycharm 中的谷歌应用程序引擎