Asociaciones genéricas en SQLAlchemy con múltiples padres

Estoy tratando de entender el "Discriminador por asociación" ejemplo enviado con SQLAlchemy, que define la combinación de HasAddresses para que cada modelo que subclasifique HasAddresses obtenga mágicamente un addresses atributo, que es una colección a la que se pueden agregar objetos de dirección. La vinculación se realiza a través de una tabla intermedia, por lo que a primera vista la relación parece de muchos a muchos, esperaba poder tener varias direcciones vinculadas a un cliente, Y también varios clientes y proveedores vinculados a una dirección.

La Address El modelo, sin embargo, está configurado de tal manera que tiene un solo parent atributo que solo puede hacer referencia a un único objeto. Entonces, en el ejemplo, una Dirección solo se puede vincular a un único Cliente o Proveedor.

¿Cómo modifico ese ejemplo para que Dirección pueda hacer una referencia inversa de varios objetos principales?

preguntado el 22 de mayo de 12 a las 10:05

1 Respuestas

podemos modificar sqlalchemy/examples/generic_associations/table_per_association.py para agregar un backref con nombre a Address, luego una @property que acumula todos los backrefs creados.

"""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

contestado el 23 de mayo de 12 a las 16:05

Sí, tenía la sospecha de que table_per_association es un mejor punto de partida. Muchísimas gracias. - Sergey

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.