在 model 和 model 之間,經常要建立對應的關聯,rails 也提供很多 Supports 的 helper
1
2
3
4
5
6
7
8
9
10
11
12
| class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
end
class Relationship < ActiveRecord::Base
belongs_to :followed, :class_name => "User"
belongs_to :follower, :class_name => "User"
end
|
可以用這個方式,自己關聯自己,像是要上面,User 可以 follower 很多個 User
rails 慣例是 model 名稱,所以不用另外加 class_name
rails 外鍵慣例是關聯的 Model 名稱加上 _id 後綴
設定當物件刪除時,如何處理依賴它的資料
1
2
3
4
5
6
7
8
9
| class Event < ActiveRecord::Base
has_many :attendees, :dependent => :destroy
end
#:destroy 把依賴的attendees也一併刪除,並且執行Attendee的destroy回呼
#:delete 把依賴的attendees也一併刪除,但不執行Attendee的destroy回呼
#:nullify 這是預設值,不會幫忙刪除attendees,但會把attendees的外部鍵event_id都設成NULL
#:restrict_with_exception 如果有任何依賴的attendees資料,則連event都不允許刪除。執行刪除時會丟出錯誤例外ActiveRecord::DeleteRestrictionError。
#:restrict_with_error 不允許刪除。執行刪除時會回傳false,在@event.errors中會留有錯誤訊息。
|
搭配through設定使用,當關聯的名稱不一致的時候,需要加上source指名是哪一種物件。
source
主要是設定 join
model 所設定的,belongs_to,指定是要關聯到哪一個
1
2
3
4
5
6
7
8
9
| class Book < ActiveRecord::Base
has_many :pages
end
class Page < ActiveRecord::Base
belongs_to :book, :counter_cache => true
end
#設定成 ture,就會自動去找 pages_count 欄位,若要指定欄位則是 counter_cache: :count_of_pages
|
關聯另一端的關聯名稱。
belongs_to 無法與 :polymorphic 同時使用。
has_one 無法與 :through 或 :as 同時使用。
1
2
3
4
5
6
7
8
9
10
11
| class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
class Article < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class Photo < ActiveRecord::Base
has_many :comments, :as => :commentable
end
|
touch
touch 為 true 時,儲存或刪除關聯物件時,關聯物件的 updated_at 或 updated_on 的時間戳會自動設成當前時間
1
2
3
4
| class Order < ActiveRecord::Base
belongs_to :customer, touch: true
#更改欄位 touch: :orders_updated_at
end
|
Scope
1
2
3
| class Order < ActiveRecord::Base
belongs_to :customer, -> { where active: true }
end
|
經常性使用 @line_item.order.customer
就可以加上
1
2
3
4
5
6
7
8
9
10
11
12
| class LineItem < ActiveRecord::Base
belongs_to :order, -> { includes :customer }
end
class Order < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class Customer < ActiveRecord::Base
has_many :orders
end
|
如果設定了 readonly 選項,則關聯物件取出時為唯讀。
select 方法可以覆寫用來取出關聯的 SELECT 子句。預設會取出所有欄位
has_many 額外方式
條件也可透過 Hash 指定
1
2
3
4
| class Customer < ActiveRecord::Base
has_many :confirmed_orders, -> { where confirmed: true },
class_name: "Order"
end
|
用 Hash 的 where,產生出來的記錄會自動使用 Hash 的作用域。
上例中,使用 @customer.confirmed_orders.create
或 @customer.confirmed_orders.build
會建立出 confirmed 欄位為 true 的訂單
1
| has_many :line_items, -> { group 'orders.id' }
|
1
| has_many :recent_orders, -> { order('order_date desc').limit(100) }
|
1
| has_many :orders, -> { offset(11) }
|
1
| has_many :orders, -> { order "date_confirmed DESC" }
|
1
| has_many :articles, -> { distinct }, through: :readings
|
若想確保不插入重複的資料到資料庫(這樣取出來就確定是不重複的記錄了),應該要在資料表上新增一個唯一性的索引。
舉例來說,如果有 person_articles 資料表,想確保所有文章不重複,可加入下面這個遷移
1
| add_index :person_articles, :article, unique: true
|
不要使用 include? 來確保唯一性,因為多個使用者可能同時加入文章,可能會導致競態條件(Race Condition)
1
| person.articles << article unless person.articles.include?(article)
|
extending
指定一個模組名稱,用來擴充關聯代理(association proxy)
1
2
3
4
5
6
7
8
9
10
11
12
13
| module FindRecentExtension
def find_recent
where("created_at > ?", 5.days.ago)
end
end
class Customer < ActiveRecord::Base
has_many :orders, -> { extending FindRecentExtension }
end
class Supplier < ActiveRecord::Base
has_many :deliveries, -> { extending FindRecentExtension }
end
|
關聯很多的 table 來找資料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| class Publication < ActiveRecord::Base
has_many :publication_contributors
has_many :contributors, :through => :publication_contributors
end
class Contributor < ActiveRecord::Base
has_many :publication_contributors
has_many :publications, :through => :publication_contributors
end
class PublicationContributor < ActiveRecord::Base
belongs_to :publication
belongs_to :contributor
end
|
1
2
3
4
| Publication
.joins( :publication_contributors => :contributor )
.where( :publication_contributors => {:contributor_type => "Author"},
:contributors => {:name => params[:authors]} )
|
Rails: Finding a deeply nested association with a where clause
官方文件:
has_many Association Reference
has_many Association Reference 中文
belongs_to 關聯手冊
has_one 關聯手冊
has_many 關聯
參考文件:
ActiveRecord - 資料表關聯
The 10 Most Underused ActiveRecord::Relation Methods