Flask 多对多关系中 primaryjoin/secondaryjoin 的理解帮助

3 投票
1 回答
2455 浏览
提问于 2025-04-17 20:24

我有两个类,分别是用户(User)和列表(Listing),我想建立一个多对多的关系,这样一个用户可以有很多喜欢的列表,而任何一个列表也可以被很多用户喜欢。

我一直在参考这个链接,但这是我第一次处理多对多的关系,所以任何帮助都非常感谢。

出现了错误:InvalidRequestError:一个或多个映射器初始化失败 - 无法继续初始化其他映射器。原始异常是:无法确定主连接条件 'favorites_table.user_id = :user_id_1' 的关系方向,关系是 User.favorites。请确保引用的列对象有外键,或者是其父表上的外键约束的一部分,或者为这个关系指定 foreign_keys 参数。

models.py

favorites_table = db.Table('favorites_table',
  db.Column('user_id', db.Integer, db.ForeignKey('listing.id')),
  db.Column('listing_id', db.Integer, db.ForeignKey('user.id'))
)

class User(db.Model):
  id = db.Column(db.Integer, primary_key = True)
  listings = db.relationship('Listing', backref = 'manager', lazy = 'dynamic')
  favorites  = db.relationship('Listing',
    secondary=favorites_table,
    primaryjoin = ('favorites_table.c.user_id == id'), 
    secondaryjoin = ('favorites_table.c.listing_id == id'),
    backref = db.backref('user', lazy = 'dynamic'),
    lazy = 'dynamic')

  def favorite_listing(self, listing):
    if not self.is_favorite(listing):
    self.favorites.append(listing)
    return self

  def unfavorite_listing(self, listing):
    if self.is_favorite(listing):
    self.favorites.remove(listing)
    return self

  def is_favorite(self, listing):
    return self.favorites.filter(favorites_table.c.listing_id == listing.id).count() > 0


class Listing(db.Model):
  id = db.Column(db.Integer, primary_key = True)
  user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

1 个回答

3

因为你的模型里已经指定了所有需要的 ForeignKey(外键),所以sqlalchemy很聪明,能够自己搞定 primaryjoinsecondaryjoin 的参数。因此,这样写是完全可以的:

favorites  = db.relationship('Listing',
    secondary = favorites_table,
    # primaryjoin = 'favorites_table.c.user_id == User.id',
    # secondaryjoin = 'favorites_table.c.listing_id == Listing.id',
    backref = db.backref('users', lazy = 'dynamic'),
    lazy = 'dynamic',
    )

如果你真的想要明确一点,可以把上面那两行注释去掉,结果是一样的。注意,我在每个 id 列前面加了模型名。

请注意,在你的 favorites_table 关系表中,列 user_id 指向的是 Listing.id,而 listing_id 指向的是 User.id,看起来应该是反过来的。

撰写回答