RubyRuby on RailsRuby、Rails知识

rails 中 polymorphic 的使用, 以及获取 un

2017-10-25  本文已影响17人  云莉6

注意:本文小写的变量都为实例变量,如:employee

使用 polymorphic

场景: EmployeeProducthas_many pictures, 但是又不希望创建两个类似的 pictures 表。也就是我们希望EmployeeProduct 可以同时关联上 Picture, 最后能从 pictures 表中各取所需,也可以在 Picture 的每个实例对象中获得所属对象。最终结果:

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_idimageable_type 对应,如果你的是 object_idobject_type,那么 model 中应该是 belongs_to :object, polymorphic: true

覆盖 Picture 中的 imageable

上述代码中,Productscope,当你用 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

  1. 一开始文中写的是 scope ,是写错了,现在改正过来了。
  2. 最好不使用 default_scope
  3. 如果非要使用 default_scope 可以用比文中更好一点的解决方法:
    belongs_to :imageable, -> { unscope(:where) }, polymorphic: true # 后来发现 bug,此方法暂定
    
    文中的方法会导致 includes 无效等问题。
上一篇下一篇

猜你喜欢

热点阅读