Late evaluation of "secondaryjoin" #11359
-
Hello When a secondaryjoin uses an aliased query, I wonder how I can use late evaluation of the relationship. from sqlalchemy import Integer, Table, Column, ForeignKey, \
create_engine, String, select
from sqlalchemy.orm import Session, relationship
from sqlalchemy.ext.declarative import declarative_base
Base= declarative_base()
friendship = Table(
'friendships', Base.metadata,
Column('friend_a_id', Integer, ForeignKey('users.id'),
primary_key=True),
Column('friend_b_id', Integer, ForeignKey('users.id'),
primary_key=True)
)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
# this relationship is used for persistence
friends = relationship("User", secondary=friendship,
primaryjoin=id==friendship.c.friend_a_id,
secondaryjoin=id==friendship.c.friend_b_id,
)
def __repr__(self):
return "User(%r)" % self.name
# this relationship is viewonly and selects across the union of all
# friends
friendship_union = select([
friendship.c.friend_a_id,
friendship.c.friend_b_id
]).union(
select([
friendship.c.friend_b_id,
friendship.c.friend_a_id]
)
).alias()
User.all_friends = relationship('User',
secondary=friendship_union,
primaryjoin=User.id==friendship_union.c.friend_a_id,
secondaryjoin=User.id==friendship_union.c.friend_b_id,
viewonly=True)
e = create_engine("sqlite://",echo=True)
Base.metadata.create_all(e)
s = Session(e)
u1, u2, u3, u4, u5 = User(name='u1'), User(name='u2'), \
User(name='u3'), User(name='u4'), User(name='u5')
u1.friends = [u2, u3]
u4.friends = [u2, u5]
u3.friends.append(u5)
s.add_all([u1, u2, u3, u4, u5])
s.commit()
print u2.all_friends
print u5.all_friends |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 7 replies
-
Hi, I would suggest you start by looking at the documentation https://docs.sqlalchemy.org/en/20/orm/relationship_api.html#sqlalchemy.orm.relationship.params.primaryjoin |
Beta Was this translation helpful? Give feedback.
-
Okay, I have found a way, probably not the most elegant one but it seems to work. from functools import cache
# ...
class User(Base):
# ...
@cache
@staticmethod
def _friendship_union():
return (
select(friendship.c.friend_a_id, friendship.c.friend_b_id)
).union(
select(friendship.c.friend_b_id, friendship.c.friend_a_id)
).alias()
all_friends = relationship(
'User',
secondary=lambda: User._friendship_union(),
primaryjoin=lambda: User.id == User._friendship_union().c.friend_a_id,
secondaryjoin=lambda: User.id == User._friendship_union().c.friend_b_id,
viewonly=True) |
Beta Was this translation helpful? Give feedback.
-
you're using Python 2 with what SQLAlchemy version? your program at the top runs without issue in SQLAlchemy 1.4 with Python 3, there's nothing I can see wrong with it |
Beta Was this translation helpful? Give feedback.
Okay, I have found a way, probably not the most elegant one but it seems to work.
I store the aliased join in a cached static method, and reuse it in multiple lambdas: