Here is my model:
user_map = Table(
"user_map",
Column('user_id', Integer, ForeignKey('user.id'), primary_key=True),
Column('map_id', Integer, ForeignKey('map.id'), primary_key=True),
PrimaryKeyConstraint('user_id', 'map_id', name='pk_user_map')
)
class Map(Base):
id = Column(Integer, primary_key=True)
name = Column(String)
owner_id = Column(Integer, ForeignKey('user.id'))
shared_maps = relationship(
'User',
secondary=user_map,
backref=backref('maps', lazy='dynamic')
)
class User(Base):
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String, unique=True)
shared_maps = Map.query.filter(Map.shared_maps.any()).all()
I want to query the user_map table, using the join condition "Map.id == user_map.map_id", but SQLAlchemy is trying to join using "Map.id == user_map.map_id and Map.owner_id == user_map.user_id". How can I specify my join condition?
I tried to use primaryjoin attribute in the relationship and to specify the condition inside the .join() but without success. Thanks in advance!
Based on your code, I've rebuilt the setup; I guess your relationship
s were mixed up. Furthermore, I've hardly ever seen primary keys (or PrimaryKeyConstraints
) in sqlalchemy's many-to-many association tables. It may make sense from a non-orm perspective, but as far as I know, it is unusual or even not required at all.
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker
Base = declarative_base()
UsersXMaps = sa.Table(
'users_x_maps',
Base.metadata,
sa.Column('user', sa.Integer, sa.ForeignKey('users.id')),
sa.Column('map', sa.Integer, sa.ForeignKey('maps.id'))
)
class User(Base):
__tablename__ = 'users'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String)
mail = sa.Column(sa.String, unique=True)
own_maps = relationship('Map', back_populates='owner')
maps = relationship(
'Map',
secondary=UsersXMaps,
back_populates='users'
)
def __str__(self):
return '{} ({}) with {} maps'.format(
self.name, self.mail, len(self.own_maps))
class Map(Base):
__tablename__ = 'maps'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String)
owner_id = sa.Column(sa.Integer, sa.ForeignKey('users.id'))
owner = relationship('User', back_populates='own_maps')
users = relationship(
'User',
secondary=UsersXMaps,
back_populates='maps'
)
def __str__(self):
return '{} (by {})'.format(self.name, self.owner.name)
So far for the setup; I've extended it a bit for proper output when printing strings. Additionally, your Map.shared_maps
relationship actually refers to User
s, not Map
s, so I also renamed that one.
When binding your association table to the two classes, you can refer to it from both sides (even though back_populates
seems to overwrite/replace the original definition) - this simplifies joins from either side.
Executing the following works as expected:
if __name__ == '__main__':
engine = sa.create_engine('sqlite:///usermaps.db')
sfactory = sessionmaker(engine)
session = sfactory()
Base.metadata.create_all(bind=engine)
bilbo = User(id=1, name='Bilbo', mail='bilbo@shire.nz')
frodo = User(id=2, name='Frodo', mail='frodo@shire.nz')
mordor = Map(id=1, name='Mordor', owner=frodo, users=[bilbo, frodo])
gondor = Map(id=2, name='Gondor', owner=bilbo, users=[bilbo, frodo])
rohan = Map(id=3, name='Rohan', owner=bilbo, users=[bilbo, frodo])
session.add_all([frodo, bilbo, mordor, gondor, rohan])
session.commit()
print('Maps by owner:')
for owner in [bilbo, frodo]:
print(owner)
for item in session.query(Map).filter(Map.owner == owner).all():
print(' - ' + str(item))
print('Maps by users:')
for item in session.query(Map).filter(Map.users.any()).all():
print(' - ' + str(item))
The output is:
Maps by owner:
Bilbo (bilbo@shire.nz) with 2 maps
- Gondor (by Bilbo)
- Rohan (by Bilbo)
Frodo (frodo@shire.nz) with 1 maps
- Mordor (by Frodo)
Maps by users:
- Mordor (by Frodo)
- Gondor (by Bilbo)
- Rohan (by Bilbo)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.