關於Rails的模型自關聯有一個非常有意思的題目,大概是這樣的:
lisa = Person.create(name:'Lisa')
tom = Person.create(name:'Tom',parent_id:lisa.id)
andy = Person.create(name:'Andy',parent_id:lisa.id)
tom.parent.name => 'Lisa'
lisa.children.map(&:name) => ['Tom','Andy']
thomas = Person.create(name: 'Thomas',parent_id: tom.id)
peter = Person.create(name:'Peter',parent_id:tom.id)
gavin = Person.create(name:'Gavin', parent_id: andy.id)
lisa.grandchildren.map(&:name) => ['Thomas','Peter','Gavin']
問如何定義Person模型來滿足以上需求?
題目考察了對模型自關聯的理解,通過審題我們可以得出以下幾點:
- Person對象的Parent同樣是Person對象(自關聯)
- Person對象對Parent是多對一關係
- Person對象對Children是一對多關係
- Person對象通過Children與GrandChildren建立了一對多關係
在不考慮GrandChildren時,不難得出模型定義如下:
class Person < ActiveRecord::Base
belongs_to :parent, class_name: 'Person', foreign_key: 'parent_id'
has_many :children, class_name: 'Person', foreign_key: 'parent_id'
end
其中Person包含兩個自關聯關係:
- 第一個就是非常常見的從子到父的關係,在Person對象創建時指定parent_id來指向父對象;
- 第二個關係用來指定Person對象對應的所有子對象
接下來更近一步,我們要找到Person對象子對象的子對象,換句話說:孫子對象。
如我們上面的分析,Person對象通過Children與GrandChildren建立了一對多關係,其代碼表現爲:
has_many :grandchildren, :through => :children, :source => :children
:source
選項的官方文檔說明如下:
The :source option specifies the source association name for a has_many :through association. You only need to use this option if the name of the source association cannot be automatically inferred from the association name. —— rails guide
在這裏我們通過:source
選項告訴Rails在children對象上查找children關聯關係。
於是該題目完整的模型定義如下:
class Person < ActiveRecord::Base
belongs_to :parent, class_name: 'Person', foreign_key: 'parent_id'
has_many :children, class_name: 'Person', foreign_key: 'parent_id'
has_many :grandchildren, :through => :children, :source => :children
end
參考:Need help to understand :source option of has_one/has_many through of Rails