iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 18
0
自我挑戰組

Junior Ruby on Rails 工程師的心得與自我挑戰 30 天 (單身狗轉移注意力之歷練)系列 第 18

[Day18] Ruby on Rails - Model 關聯性, 參數補充說明

  • 分享至 

  • xImage
  •  

大綱

  • class_name、foreign_key and primary_key
  • source
  • scope 參數
  • inverse_of
  • dependent: :destroy、dependent: :nullify
  • belongs_to optional: true

class_name、foreign_key and primary_key

  • 一個使用者可以開很多家商店。
  • 每個商店屬於一個使用者。
# 檔案:app/models/user.rb
class User < ApplicationRecord
  has_many :stores
end
# 檔案:app/models/store.rb
class Store < ApplicationRecord
  belongs_to :user
end

Store model 裡面的 foreign key 是 user_id 的話,便可以順利的將這兩個 model 關聯起來。但是如果我們想要將 store.user 改成 store.owner 呢?由於很多參數 Rails 都幫我們預設好了,因此如果我們改寫的話就要將那些參數補進去。

請參考修正如下:

# 檔案:app/models/user.rb
class User < ApplicationRecord
  has_many :stores
end
# 檔案:app/models/store.rb
class Store < ApplicationRecord
  belongs_to :owner, class_name: 'User', primary_key:  :id, foreign_key:  :user_id
end

透過上述 Store Model 程式碼的修正,就能將原先 user 與 store 的關聯,改成 owner 與 store 的關聯。

Source
這個運用的情境,通常是 Model 間的多對多關係時。

請看以下範例:

# 檔案:app/models/drink_store.rb
class Drink < ApplicationRecord
  belongs_to :user

  has_many :drink_store_menus
  has_many :drinks, through:  :drink_store_menus
end
# 檔案:app/models/drink.rb
class Drink < ApplicationRecord
  has_many :drink_store_menus
  has_many :drink_stores, through:  :drink_store_menus
end

如果需要把 drink.drink_stores 改寫成 drink.drink_areas 的話,我們就得指定 source,因為 Rails 原本是預設會去中間表找 drink_store 欄位,而我們現在改成 drink_area 就得加入 source: drink_store。

請參考修正如下:

# 檔案:app/models/drink_store.rb
class Drink < ApplicationRecord
  belongs_to :user

  has_many :drink_store_menus
  has_many :drinks, through:  :drink_store_menus
end
# 檔案:app/models/drink.rb
class Drink < ApplicationRecord
  has_many :drink_store_menus
  has_many :drink_areas, through:  :drink_store_menus, source: :drink_stores
end

Scope 參數
當想要在關聯裡面撈出特定的資料的時候,可以透過 scope 參數來訂定找資料的規則。

請看以下範例:

# 檔案:app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user
end
# 檔案:app/models/user.rb
class User < ApplicationRecord
  has_many :posts, dependent: :destroy
  has_many :self_posts, -> { where(draft: false) }, class_name:  'Post', dependent: :destroy
end

從上面的範例可以看到我們撈兩種不同的 post 資料,一種是全部類型的 post,另外一種是 draft 為 false 的 post。透過在第二個參數加入 -> { where(draft: false) },我們就可以輕易地在 Model 層進行篩選資料的動作了!

inverse_of
簡單的來說就是兩點:

  • 讓兩個互相關聯的 model 可以指到同一個 instance 物件,在 model 的資料有更新時讓其他有關聯的 model可以即時更新對方的資料。
  • 減少資料重複查詢。

dependent: :destroy、dependent: :nullify

  • 加上 dependent: :destroy 參數的原因,是當如果要刪除候選人資料的時候,那些投給它的票也可以順利被刪除。
  • 加上 dependent: :nullify 參數的原因,不會幫忙刪除 vote_logs,但會把vote_logs 的外部鍵 candidate_id 都設成 NULL

請看以下範例:

# 檔案:app/models/candidate.rb
class Candidate < ApplicationRecord
  has_many :vote_logs, dependent: :destroy
end

belongs_to optional: true
透過 optional: true 可以允許 event 沒有 category 的情況。

請看以下範例:

# 檔案:app/models/post.rb
class Event < ApplicationRecord
  belongs_to :category, optional: true
end

上一篇
[Day17] Ruby on Rails - Model 關聯性,多對多
下一篇
[Day19] Ruby on Rails - Model 資料驗證
系列文
Junior Ruby on Rails 工程師的心得與自我挑戰 30 天 (單身狗轉移注意力之歷練)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言