3

HasAddressesミックスインを定義するSQLAlchemyに同梱されている「 Discriminatoronassociation 」の例に頭を悩ませようとしています。これにより、HasAddressesをサブクラス化する各モデルaddressesは、Addressオブジェクトを追加できるコレクションである属性を魔法のように取得します。リンクは中間テーブルを介して実行されるため、一見すると関係は多対多のように見えます。複数のアドレスを顧客にリンクし、複数の顧客とサプライヤーをアドレスにリンクできるようにしたいと思いました。

ただし、モデルは、単一のオブジェクトのみを参照できるAddress単一の属性を持つように設定されています。parentしたがって、この例では、アドレスは単一の顧客またはサプライヤーにのみリンクできます。

その例を変更して、Addressが複数の親オブジェクトを逆参照できるようにするにはどうすればよいですか?

4

1 に答える 1

6

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
于 2012-05-23T15:37:56.917 に答える