rails 中 polymorphic 的使用, 以及获取 un
2017-10-25 本文已影响17人
云莉6
注意:本文小写的变量都为实例变量,如:employee
使用 polymorphic
场景: Employee
和 Product
都 has_many pictures
, 但是又不希望创建两个类似的 pictures
表。也就是我们希望Employee
和 Product
可以同时关联上 Picture
, 最后能从 pictures
表中各取所需,也可以在 Picture
的每个实例对象中获得所属对象。最终结果:
- rails 中
employee.pictures
product.pictures
picture.imageable # 是一个具体的 employee 或者 product 对象
- 最终存在表里的是:
id | name | imageable_id | imageable_type |
---|---|---|---|
1 | sun | 1 | Product |
2 | moon | 1 | Employee |
- 示例代码
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable
default_scope { where(published: true) }
end
class CreatePictures < ActiveRecord::Migration[5.0]
def change
create_table :pictures do |t|
t.string :name
t.integer :imageable_id
t.string :imageable_type
t.timestamps
end
add_index :pictures, [:imageable_type, :imageable_id]
end
end
- 注意事项
model
中的 imageable
需要与表中的 imageable_id
和 imageable_type
对应,如果你的是 object_id
和 object_type
,那么 model
中应该是 belongs_to :object, polymorphic: true
覆盖 Picture 中的 imageable
上述代码中,Product
有 scope
,当你用 picture.imageable
的时候,会把 scope 也带上,有可能你需要的是所有的 imageable ,而不是 scope 下的,这个时候可以通过覆盖掉 imageable 这个方法来解决。
比如:
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
def imageable
@imageable ||= imageable_type.constantize.unscoped.find_by_id imageable_id
end
end
这里面通过 imageable_type
获取类名,constantize
把字符串的类名转化为对象,再调用 unscoped
方法,最后通过 imageable_id
找到具体的 imageable
对象。定义 @imageable
是为了一次请求中不多次查询imageable
,因为 rails
会缓存。
根据 RubyChina 上的反馈做出调整
参考:https://ruby-china.org/topics/34429
- 一开始文中写的是 scope ,是写错了,现在改正过来了。
- 最好不使用 default_scope
- 如果非要使用 default_scope 可以用比文中更好一点的解决方法:
文中的方法会导致 includes 无效等问题。belongs_to :imageable, -> { unscope(:where) }, polymorphic: true # 后来发现 bug,此方法暂定