首先是has_many through和polymorphice的用法
has_many model_names,through: model_names
belongs_to model_nameable,polymorphic:true
以人user养宠物pet为例
一个人可以养多个宠物,但是宠物又分种类 猫 狗等等
那么可以建立如下4个模型
class User <ActiveRecord::Base
has_many :pets
has_many :cats,through: :pets
has_many :dogs,through: :pets
end
class Pet <ActiveRecord::Base
belongs_to :user
belongs_to :cat
belongs_to :dog
end
class Cat <ActiveRecord::Base
end
class Dog <ActiveRecord::Base
end
那么之后user可以调用cats dogs方法获取养的小猫小猪,但是有一个问题,如果希望一次性获取养的全部宠物的集合该怎么办?
答案应该是user.cats + user.dogs
但是如果养的宠物种类特别多的时候,这样做就显得麻烦了,而且数据处理起来很麻烦
于是这里就可以引入多态关联了,pet和cat dog就刚好是典型的多态关联,一个pet对象它对应的宠物有可能是cat也有可能是dog,但是cat和dog都对应这pet
这样的关联可以用多态来取代
class Pet
belongs_to :pettable,polymorphic: true
end
并且在pet表中增加pettable_id(integer) pettable_type(string)两个字段
这时可以直接通过pet.pettable,获取cat dog对象
将上述两种关联结合起来,各个模型如下所示
class User <ActiveRecord::Base
has_many :pets
has_many :cats,through: :pets,source_type: 'Cat',source: 'pettable'
has_many :dogs,through: :pets,source_type: 'Dog',source: 'pettable'
end
class Pet <ActiveRecord::Base
belongs_to :user
belongs_to :pettable,polymorphic: true
end
User模型中source_type source是必须要指明的,在has_many through 关联是通过一个多态模型去关联其他模型的时候,只有指定了source和source_type这样才能够程序正确的模型对象,不然,像user.cats这样的调用的话,rails会去寻找Pet::Cat这样的关联,在sql语句中会去寻找pettable_type为pet的,但是实际上Pet只关联了pettable,所以会找不
到。
按照上述方式定义之后,就可以通过user.cats user.dogs找到其下的各种宠物了,最关键的是,可以更为方便的获取所有种类宠物的集合
直接通过 user.pets.pettable
结果就类似于 [Cat: {},Dog: {},Dog: {},Cat: {}...]这样一个数组,所有类型的宠物都在里面了,在子关联模型非常多的时候,非常有用