layout: post
title: 第十一章困惑 外鍵 及 反轉(zhuǎn)relationships表
categories:
- rails
tags: - railstutorail 學(xué)習(xí)筆記
不知道有誰學(xué)到11章時和我一樣困惑的累贤,所謂的對調(diào)兩列的位置钞翔,組建成reverse_ralationships表究竟是什么意思。畢竟表根本沒變就是原來那個表阿笤虫,已經(jīng)完整的體現(xiàn)了用戶間關(guān)系。誰是關(guān)注誰是被關(guān)注根本沒變先蒋,追人的沒突然變成被追啊隅津,攜帶信息完全一樣。
沒錯稚机,真相就是:其實表就是原來的表,根本沒變获搏。表沒有反轉(zhuǎn)赖条,反轉(zhuǎn)的是外鍵,從而反轉(zhuǎn)查詢方向常熙。因為Users表和relationships表通過外鍵一一對應(yīng)纬乍,所以把表重命是為了另設(shè)外鍵。根本就沒有任何將表進行反轉(zhuǎn)的處理裸卫。仿贬。你把User.rb中的reverse_relationships 都改成 same_relationships 試試,結(jié)果一樣墓贿。取名reverse, 其實是外鍵和查詢方向的reverse茧泪。
先說外鍵
第十章的user.microposts比較簡單, 生成micropost時無須指定外鍵,因為micopost 模型中就是user_id聋袋,只要正確定義了用戶和微博之間的關(guān)聯(lián)關(guān)系(has_many,belongs_to),使用user.microposts.build創(chuàng)建新微薄時队伟,rails 會自動將micropost中的user_id賦值為相應(yīng)的user.id。
運行驗證一下幽勒,rails console --sandbox:
1.9.3-p429 :001 > User.first.microposts.build
會新建一個micropost嗜侮,micropost的user_id屬性 自動賦值為 user.id (此例為User.first.id, "1"),其他屬性為nil,待給參數(shù)
User Load (0.5ms) SELECT "users".* FROM "users" LIMIT 1
=> #<Micropost id: nil, content: nil, user_id: 1, created_at: nil, updated_at: nil>
而relationship表中沒有叫user_id 的,只有對應(yīng)User的follower_id和followed_id,所以User表必須通過指定的外鍵和relationship表一一對應(yīng)锈颗。
書中先指定的外鍵是follower_id(你完全可以先指定followed_id)缠借,那么使用 user.relationships.build(followed_id: ...)建立relationship時,被設(shè)為外鍵的follower_id 會被自動賦對應(yīng)的 user.id值宜猜,而followed_id是你給的參數(shù),從而得到relationship表中的一行具體的relationship
1.9.3-p429 :002 > User.first.relationships
會列出 User.first 的所有的 follower_id =1的 relationships表
User Load (0.4ms) SELECT "users".* FROM "users" LIMIT 1
Relationship Load (46.7ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = 1
=> [#<Relationship id: 21, follower_id: 1, followed_id: 2, created_at: "2013-07-21 13:52:10", updated_at: "2013-07-21 13:52:10">]
1.9.3-p429 :003 > User.first.relationships.build
會新建一個relationship, 其中follower_id 是User指定的外鍵硝逢,被賦值User.first.id,而followed_id則是nil,待給參數(shù)
User Load (0.4ms) SELECT "users".* FROM "users" LIMIT 1
=> #<Relationship id: nil, follower_id: 1, followed_id: nil, created_at: nil, updated_at: nil>
1.9.3-p429 :004 > User.first.relationships.build(followed_id: 2)
(想讓用戶1關(guān)注用戶2姨拥,傳入?yún)?shù)。注意沒save,relationship_id 為nil渠鸽。要save 用 User.first.relationships.create!(followed_id: 2))叫乌。
User Load (0.4ms) SELECT "users".* FROM "users" LIMIT 1
=> #<Relationship id: nil, follower_id: 1, followed_id: 2, created_at: nil, updated_at: nil>
relationship表建立后,就可以通過
has_many:followeds,through: :relationships
使用followed_id 生成 user.followeds 數(shù)組徽缚。為了好聽憨奸,書中用
has_many:followed_users, through: :relationships, source: :followed
把user.followeds改成了 user.followed_users
user.followed_users 通過relationships表將 follower_id =(user.id) 所有對應(yīng)的 followed_id 從數(shù)據(jù)庫中找出生成數(shù)組。即該user的“關(guān)注的人”列表凿试。
(例如下面我找User.first所有關(guān)注的人排宰,即查詢follower_id = 1 對應(yīng)的 followed_id)
運行rails console --sandbox:
1.9.3-p429 :005 > User.first.followed_users
得到了User.first 的所有關(guān)注的人(我這只有一個,User.id=2的, 說明User1 只關(guān)注了 User2)那婉。
User Load (0.4ms) SELECT "users".* FROM "users" LIMIT 1
User Load (0.4ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = 1
=> [#<User id: 2, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2013-07-21 10:50:11", updated_at: "2013-07-21 10:50:11", password_digest: "$2a$.....", remember_token: "9c2eccc....."]
困惑的revese_relationships表
上面說明了指定了follower_id為外鍵的情況
所以User.rb模型中這兩句的意思就是
has_many :relationships,foreign_key:"follower_id",dependent: :destroy
對于relationships表板甘,User_id固定對應(yīng)follower_id,所以使用user.relationships得到的是所有 follower_id值為user_id的 relationship數(shù)組
has_many :followed_users, through: :relationships, source: :followed
通過表查詢對應(yīng)的followed_id擁有了很多followed_users详炬。所以使用user.followed_users可得到該user的followed_users數(shù)組盐类,即在追哪些人。
下面說怎么通過關(guān)系表(再次強調(diào)呛谜,就是一張表) 查找出該user所有的“粉絲”列表在跳。
通過調(diào)換 followed_id為外鍵(所以通過表查詢的方向就反過來了,現(xiàn)在固定的是followed_id隐岛,來查詢表中對應(yīng)的follower_id數(shù)組猫妙。(比如對于user2, user.followers則通過relationships表 將 followed_id =2 對應(yīng)的所有 follower_id 找出來,即user2的粉絲數(shù)組)
所以User.rb模型中這兩句的意思就是
has_many :same_relationships, foreign_key: "followed_id",class_name: "Relationship",dependent: :destroy
對于same_relationships表(呵呵礼仗,不用reverse試試),User_id固定對應(yīng)于followed_id,所以使用user.same_relationships得到的是所有 followed_id值為user_id的 relationship數(shù)組
has_many :followers, through: :same_relationships, source: :follower
通過表查詢對應(yīng)的follower_id擁有了很多followers.所以使用user.followers可得到該user的followers數(shù)組吐咳,即粉絲團)
(外鍵和表一一對應(yīng),所以表得改名,否則查詢關(guān)系就沖突了元践,user.relationships 不知道user_id賦值給誰查詢韭脊。)
(其實認真的看看下面4句中的SELECT語句就明白了,全是通過relationship表单旁,變的只是查詢的外鍵值)
1.9.3-p429 :006 > user2 = User.find(2)
1.9.3-p429 :007 > user2.relationships Relationship Load (0.2ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = 2 => [#<Relationship id: 24, follower_id: 2, followed_id: 1, created_at: "2013-07-29 13:29:56", updated_at: "2013-07-29 13:29:56">]
1.9.3-p429 :008 > user2.same_relationships
Relationship Load (30.8ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."followed_id" = 2
=> [#<Relationship id: 21, follower_id: 1, followed_id: 2, created_at: "2013-07-21 13:52:10", updated_at: "2013-07-21 13:52:10">]
1.9.3-p429 :009 > user2.followers User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = 2 => [#<User id: 1, name: "lafmad", email: "inaircastle@gmail.com", created_at: "2013-07-16 14:01:47", updated_at: "2013-07-20 03:14:16", password_digest: "$2a$10$....", remember_token: "tRLqtITZxX..."]
1.9.3-p429 :010 > user2.followed_users User Load (0.5ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = 2 => [#<User id: 1, name: "lafmad", email: "inaircastle@gmail.com", created_at: "2013-07-16 14:01:47", updated_at: "2013-07-20 03:14:16", password_digest: "$2a$10$m5bBDpsDXDv......", remember_token: "tRLqtIT..."]
寫完了我是更明白了沪羔,如果有讀者的話,希望沒把你搞的更糊涂。沉住氣蔫饰,忽然間一回頭就明白了(然后又糊涂了琅豆,然后又明白了,嗯嗯篓吁。)
以上