使用數據庫聯結查詢所關注用戶的文章
程序首頁目前按時間降序顯示數據庫中的所有文章,現在我們已經完成了關注功能,如果讓用戶選擇只查看所關注用戶發佈的博客文章就更好了
若想顯示所關注用戶發佈的所有文章,第一步顯然先要獲取這些用戶,然後獲取各用戶的文章,再按一定順序排列,寫入單獨列表,可是這種方式的伸縮性不好,隨着數據庫不斷變大,生成這個列表的工作量也不斷增長,而且分頁等操作也無法高效率完成,獲取博客文章的高效方式是隻用一次查詢
完成這個操作的數據庫操作稱爲聯結,聯結操作用到兩個或更多的數據表,在其中查找滿足指定條件的記錄組合,再把記錄組合插入一個臨時表中,這個臨時表就是聯結查詢的結果,理解聯結查詢的最好方式是實例講解
下表是一個users表實例,表中有3個用戶
下表是對應的posts表,表中有幾篇博客文章
最後,下面follows表顯示了誰關注誰,從這個表中你可以看出,john關注了david,susan關注了john,但david誰也沒關注
若想獲得susan所關注用戶發佈的文章,就要合併posts
表和follows
表,首先過濾follows表,只留下關注者爲susan的記錄,即上表中的最後兩行,然後過濾posts表,留下author_id
和過濾後的follows表中followed_id
相等的記錄,把兩次過濾結果合併,組成臨時聯結表,這就能高效查詢susan所關注用戶的文章列表,下表是聯結操作的結果,表中用來執行鏈接操作的列用*
標記:
id | author_id* | body | followed_id | followed_id* |
---|---|---|---|---|
2 | 1 | john的博客文章 | 2 | 1 |
3 | 3 | david的博客文章 | 2 | 3 |
4 | 1 | john的第二篇博客文章 | 2 | 1 |
這個表中包含的博客文章都是用戶susan所關注用戶發佈的,使用Flask-SQLAlchemy執行這個聯結操作的查詢相當複雜:
return db.session.query(Post).select_from(Follow).\
filter_by(follower_id=self.id).\
join(Post, Follow.followed_id == Post.author_id)
在此之前見到的查詢都是從所查詢模型的query屬性開始的,這種查詢不能在這裏使用,因爲查詢要返回posts記錄,所以首先要做的操作是在follow表上執行過濾器,因此,這裏使用了一種更基礎的查詢方式,爲了完全理解上述查詢,下面分別說明各部分:
- db.session.query(Post):指明這個查詢表要返回(Post)對象
- select_from(Follow)的意思是這個查詢從Follow模型開始
- filter_by(follower_id=self.id)使用關注用戶過濾follows表
- john(Post, Follow.followed_id == Post.author_id)聯結filter_by()得到的結果和Post對象
調換過濾器和聯結的順序可以簡化這個查詢:
return Post.query.john(Follow, Follow.followed_id == Post.author_id)\
.filter(Follow.follower_id == self.id)
如果首先執行聯結操作,那麼這個查詢就可以從Post.query
開始,此時唯一需要使用的兩個過濾器是john()
和filter()
,但這兩種查詢是一樣的嗎?先執行聯結操作再過濾看起來工作量會更大一些,但實際上這兩種查詢是等效的,SQLAlchemy首先收集所有的過濾器,然後再以最高效的方式生成查詢,這兩種查詢生成的原生SQL指令是一樣的,我們要把後一種查詢寫入Post模型,如下:
class User(db.Model):
#...
@property
def followed_posts(self):
return Post.query.join(Follow, Follow.followed_id == Post.author_id)\
.filter(Follow.follower_id == self.id)
followed_posts()
方法定義爲屬性,因此調用時無需加(),如此一來所有關係的句法都一樣了