On May 16, 2013, at 3:02 PM, Bobby Impollonia <
bob...@gmail.com> wrote:
>
> I hoped to end up with something like:
> SELECT
email_addresses.id AS email_addresses_id, email_addresses.address AS email_addresses_address
> FROM email_addresses, affiliations
> WHERE affiliations.address_type = 'email' AND affiliations.address_id =
email_addresses.id AND
affiliations.id = 1
>
> Two questions:
> 1) Is the behavior I am seeing here expected given my relationship configuration?
> 2) How can I configure my relationship to exhibit the desired behavior in the situation shown above?
Ok well lets think of it in terms of joins. What if you wanted to load all the affiliations and EmailAddresses together? the join would be:
select * from affiliations JOIN email_addresses ON affiliations.address_id=
email_addresses.id AND affiliations.address_type='email'
above, there's not really a space for "AND
affiliations.id = ...something ?", unless maybe if it were equated to itself.
So maybe, this kind of thing would be possible if you could say:
email_address = relationship(
'EmailAddress',
primaryjoin=and_(address_type == 'email',
id == lazy(id),
address_id == EmailAddress.id),
foreign_keys=[address_id],
viewonly=True)
the lazy load would need to figure out to set up a bind for one of the slot there (that's the hypothetical "lazy()" annotation). its funny we're a lot closer to that sort of thing, since we do have situations where we have things like "column == remote(column)" now, but not quite in that arrangement.
But I don't think we need to get into any of that here since your class has a discriminator anyway, we can just use inheritance so that your different Affiliation objects know what to do, see below.
#!/usr/bin/env python
from sqlalchemy import Column, Integer, String, ForeignKey, create_engine, and_
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, backref, contains_eager, remote
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
class StreetAddress(Base):
__tablename__ = 'steet_addresses'
id = Column(Integer, primary_key=True)
address = Column(String, nullable=False)
class EmailAddress(Base):
__tablename__ = 'email_addresses'
id = Column(Integer, primary_key=True)
address = Column(String, nullable=False)
class Affiliation(Base):
__tablename__ = 'affiliations'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('
users.id'), nullable = False)
address_type = Column(String, nullable=False)
address_id = Column(Integer, nullable=False)
user = relationship("User", backref='affiliations')
street_address = None
email_address = None
__mapper_args__ = dict(polymorphic_on=address_type)
class EmailAffiliation(Affiliation):
email_address = relationship(
'EmailAddress',
primaryjoin=Affiliation.address_id == EmailAddress.id,
foreign_keys=[Affiliation.address_id],
viewonly=True)
__mapper_args__ = dict(polymorphic_identity='email')
class StreetAffiliation(Affiliation):
street_address = relationship(
'StreetAddress',
primaryjoin=Affiliation.address_id == StreetAddress.id,
foreign_keys=[Affiliation.address_id],
viewonly=True)
__mapper_args__ = dict(polymorphic_identity='street')
Base.metadata.create_all(engine)
user = User(id=1)
email = EmailAddress(id=1, address='
y...@localhost.com')
street = StreetAddress(id=1, address='123 Main str.')
session.add_all([
user,
email,
street,
])
session.flush()
street_affiliation = StreetAffiliation(user=user, address_id=1)
email_affiliation = EmailAffiliation(user=user, address_id=1)
session.add_all([
street_affiliation,
email_affiliation,
])
session.flush()
session.commit()
assert street_affiliation.street_address is not None
assert email_affiliation.email_address is not None
assert street_affiliation.email_address is None
assert email_affiliation.street_address is None