2014-12-02 32 views
3

我使用SQL鍊金術這樣有一個簡單的多對多的關係:瓶SQLAlchemy的多對多的關係只能通過關係名訪問時返回一個結果

file_favorites = db.Table('file_favorites', 
           db.Column('file_id', db.Integer(), db.ForeignKey('file.id')), 
           db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), 
           db.Column('created_at', db.DateTime(), default=db.func.now())) 


    class File(db.Model, helpers.ModelMixin): 
     id = db.Column(db.Integer, primary_key=True, autoincrement=True) 
     name = db.Column(db.Unicode, nullable=False) 
     description = db.Column(db.Unicode, nullable=False) 
     created_at = db.Column(db.DateTime(), default=func.now()) 
     last_updated = db.Column(db.DateTime(), default=func.now(), onupdate=func.now()) 

     user_id = db.Column('user_id', db.Integer(), db.ForeignKey('user.id'), nullable=False, index=True) 
     user = db.relationship('User') 

     favorited_by = db.relationship('User', secondary=file_favorites, lazy='dynamic') 

     def is_favorite_of(self, user): 
      query = File.query 
      query = query.join(file_favorites) 
      query = query.filter(file_favorites.c.file_id == self.id) 
      query = query.filter(file_favorites.c.user_id == user.id) 
      return query.count() > 0 

     def favorite(self, user): 
      if not self.is_favorite_of(user): 
       self.favorited_by.append(user) 

     def unfavorite(self, user): 
      if self.is_favorite_of(user): 
       self.favorited_by.remove(user) 

我期望訪問favorited_by屬性將導致試圖返回已收藏此文件的用戶列表的查詢。但是,似乎查詢只訪問FIRST用戶收藏此文件。我對此感到困惑,並期望我不能正確理解sqlalchemy關係。下面是結果我遇到:

def create_model(model_class, *args, **kwargs): 
    model = model_class(*args, **kwargs) 
    db.session.add(model) 
    db.session.commit() 
    return model  

def test_favorited_by(self): 
     user = create_model(User, username='user', email='[email protected]', password='password') 
     user1 = create_model(User, username='user1', email='[email protected]', password='password') 
     user2 = create_model(User, username='user2', email='[email protected]', password='password') 


     file = create_model(File, name='file', description='a description', user=user) 

     file.favorite(user1) 
     file.favorite(user) 
     file.favorite(user2) 
     db.session.commit() 
     print file.favorited_by 

結果在此查詢:

SELECT "user".id AS user_id, "user".email AS user_email, "user".username AS user_username, "user".password AS user_password, "user".active AS user_active, "user".last_login_at AS user_last_login_at, "user".current_login_at AS user_current_login_at, "user".last_login_ip AS user_last_login_ip, "user".current_login_ip AS user_current_login_ip, "user".login_count AS user_login_count, "user".last_updated AS user_last_updated, "user".created_at AS user_created_at 
FROM "user", file_favorites 
WHERE :param_1 = file_favorites.file_id AND "user".id = file_favorites.user_id 

最終返回USER1,如果訂單被切換,第一用戶收藏該文件將永遠是用戶返回。

+0

會發生什麼事,如果你打印'file.favorited_by.all()'? – dirn 2014-12-02 21:08:20

+0

打印file.favorited_by.all()返回: [] 列表中的單個用戶對象,它與user1匹配,第一個用戶將其設置爲favorite。 – elUser 2014-12-02 21:10:03

+0

什麼'File.favorite'看起來像?我的猜測是它正在做'self.favorited_by = user'而不是'self.favorited_by.append(user)'。 – dirn 2014-12-02 21:14:52

回答

2

您的問題與is_favorite_of。它沒有將user納入檢查。你需要添加另一個過濾器。

def is_favorite_of(self, user): 
    query = File.query 
    query = query.join(file_favorites) 
    query = query.filter(file_favorites.c.file_id == self.id) 
    query = query.filter(file_favorites.c.user_id == user.id) 
    return query.count() > 0 

或者,這整個功能可以簡化爲:

def is_favorite_of(self, user): 
    return user in self.favorited_by 
+0

感謝您的答案,我也喜歡您的簡化!有趣的是,這條線原本就在那裏,我想我用一個熱鍵意外刪除了它。 – elUser 2014-12-02 21:46:10