Nyc*_*cen 32 performance activerecord ruby-on-rails polymorphic-associations
我读了这篇有趣的文章,关于使用多态来在Rails中创建更好的活动源.
我们最终得到类似的东西
class Activity < ActiveRecord::Base
belongs_to :subject, polymorphic: true
end
Run Code Online (Sandbox Code Playgroud)
现在,如果其中两个主题是例如:
class Event < ActiveRecord::Base
has_many :guests
after_create :create_activities
has_one :activity, as: :subject, dependent: :destroy
end
class Image < ActiveRecord::Base
has_many :tags
after_create :create_activities
has_one :activity, as: :subject, dependent: :destroy
end
Run Code Online (Sandbox Code Playgroud)
将create_activities定义为
def create_activities
Activity.create(subject: self)
end
Run Code Online (Sandbox Code Playgroud)
客人和标签定义为:
class Guest < ActiveRecord::Base
belongs_to :event
end
class Tag < ActiveRecord::Base
belongs_to :image
end
Run Code Online (Sandbox Code Playgroud)
如果我们查询记录的最后20个活动,我们可以这样做:
Activity.order(created_at: :desc).limit(20)
Run Code Online (Sandbox Code Playgroud)
我们有第一个N + 1查询问题,我们可以通过以下方式解决:
Activity.includes(:subject).order(created_at: :desc).limit(20)
Run Code Online (Sandbox Code Playgroud)
但是,当我们呼叫访客或标签时,我们会遇到另一个N + 1查询问题.
为了能够使用分页,解决这个问题的正确方法是什么?
Nyc*_*cen 16
编辑2:我现在使用rails 4.2和eager loading polymorphism现在是一个功能:)
编辑:这似乎在控制台中工作,但出于某种原因,我建议使用下面的部分仍然生成子弹宝石的N + 1查询堆栈警告.我需要调查......
好的,我找到了解决方案([编辑]或我做了吗?),但它假设你知道所有科目类型.
class Activity < ActiveRecord::Base
belongs_to :subject, polymorphic: true
belongs_to :event, -> { includes(:activities).where(activities: { subject_type: 'Event' }) }, foreign_key: :subject_id
belongs_to :image, -> { includes(:activities).where(activities: { subject_type: 'Image' }) }, foreign_key: :subject_id
end
Run Code Online (Sandbox Code Playgroud)
现在你可以做到
Activity.includes(:part, event: :guests, image: :tags).order(created_at: :desc).limit(10)
Run Code Online (Sandbox Code Playgroud)
但是为了急切加载工作,你必须使用例如
activity.event.guests.first
Run Code Online (Sandbox Code Playgroud)
并不是
activity.part.guests.first
Run Code Online (Sandbox Code Playgroud)
所以你可以定义一个方法来代替主题
def eager_loaded_subject
public_send(subject.class.to_s.underscore)
end
Run Code Online (Sandbox Code Playgroud)
所以现在你可以有一个视图
render partial: :subject, collection: activity
Run Code Online (Sandbox Code Playgroud)
偏爱的
# _activity.html.erb
render :partial => 'activities/' + activity.subject_type.underscore, object: activity.eager_loaded_subject
Run Code Online (Sandbox Code Playgroud)
还有两个(虚拟)部分
# _event.html.erb
<p><%= event.guests.map(&:name).join(', ') %></p>
# _image.html.erb
<p><%= image.tags.first.map(&:name).join(', ') %></p>
Run Code Online (Sandbox Code Playgroud)
这有望在rails 5.0中得到修复.已经存在问题和拉取请求.
https://github.com/rails/rails/pull/17479
https://github.com/rails/rails/issues/8005
我有分叉导轨并将补丁应用到4.2稳定,它适用于我.尽管我不能保证定期与上游同步,但请随意使用我的前叉.
https://github.com/ttosch/rails/tree/4-2-stable