Active Record 包含在 STI 中
Active Record includes with STI
我有以下型号
class Event < ActiveRecord::Base
has_many :attendances
class Attendance < ActiveRecord::Base
belongs_to :user
class Student < User
has_one :student_detail
class StudentDetail < ActiveRecord::Base
belongs_to :school
class Staff < User
has_one :staff_detail
class StaffDetail < ActiveRecord::Base
StudentDetail 和 StaffDetails 有更多信息,我试图避免将所有信息都放在一个 STI 用户 table 中,因为必须使用类似于 具体 class 的东西每个 table 模式
我可以轻松做到这一点
Event.includes(:attendances => :user).where(...)
但我希望能够根据用户类型包含
例如
Event.includes(attendances: {:user => :student_details })
这将失败,因为一些用户是 Staff 对象。
我知道 rails 不支持开箱即用,但任何人都有任何技巧可以让它工作
目前最好的解决方案是将用户按出勤率分配给学生和教职员工
即
class Attendance < ActiveRecord::Base
belongs_to :student, -> {includes(:staff_detail) }
belongs_to :staff, -> {includes(:student_detail) }
#belong_to :user
这并不理想。
有人有任何提示吗?解决这个问题的方法。
如何将 includes
作为 default_scope
放在 STI 模型上?
class Event < ActiveRecord::Base
has_many :attendances
class Attendance < ActiveRecord::Base
belongs_to :user
class Student < User
has_one :student_detail
default_scope includes(:student_detail)
class StudentDetail < ActiveRecord::Base
belongs_to :school
class Staff < User
has_one :staff_detail
default_scope includes(:staff_detail)
class StaffDetail < ActiveRecord::Base
那么我认为:
Event.includes(:attendances => :user).where(...)
应该为学生和教职员工预加载。
最简单的方法是将 has_one
关联向下移动到用户。由于只有 Staff
条记录会有 staff_details
,预加载将正常工作。
class User < ActiveRecord::Base
has_one :staff_detail
has_one :student_detail
end
class Staff < User; end
class Student < User; end
但这并不理想。要进一步自定义预加载,您可以使用 Rails 中的 Preloader
class。首先,加载所有 不包含 的记录,然后遍历它们并预加载您需要的关联:
events = Event.includes(:attendances => :user)
users = events.users.flatten
users.group_by(&:class).each do |klass, records|
associations = {
Staff: [:staff_detail],
Student: [:student_detail]
}.fetch(klass, [])
ActiveRecord::Associations::Preloader.new(records, associations).run
end
注意这个APIchanged in Rails 4. In versions 3 and earlier, you just used the preload_associations
方法。
前段时间我写了一篇 blog post about this same problem,其中包括一些其他巧妙的技巧(例如指定您获得正确的行为)。
您可以使用命名作用域让您的生活更轻松一些。
class Event < ActiveRecord::Base
has_many :attendances
scope :for_students, -> { includes(:attendances => { :users => :student_detail }).where('users.type = ?', 'Student') }
scope :for_staff, -> { includes(:attendances => { :users => :staff_detail }).where('users.type = ?', 'Staff') }
end
那你就可以Event.for_students
我有以下型号
class Event < ActiveRecord::Base
has_many :attendances
class Attendance < ActiveRecord::Base
belongs_to :user
class Student < User
has_one :student_detail
class StudentDetail < ActiveRecord::Base
belongs_to :school
class Staff < User
has_one :staff_detail
class StaffDetail < ActiveRecord::Base
StudentDetail 和 StaffDetails 有更多信息,我试图避免将所有信息都放在一个 STI 用户 table 中,因为必须使用类似于 具体 class 的东西每个 table 模式
我可以轻松做到这一点
Event.includes(:attendances => :user).where(...)
但我希望能够根据用户类型包含 例如
Event.includes(attendances: {:user => :student_details })
这将失败,因为一些用户是 Staff 对象。
我知道 rails 不支持开箱即用,但任何人都有任何技巧可以让它工作
目前最好的解决方案是将用户按出勤率分配给学生和教职员工 即
class Attendance < ActiveRecord::Base
belongs_to :student, -> {includes(:staff_detail) }
belongs_to :staff, -> {includes(:student_detail) }
#belong_to :user
这并不理想。 有人有任何提示吗?解决这个问题的方法。
如何将 includes
作为 default_scope
放在 STI 模型上?
class Event < ActiveRecord::Base
has_many :attendances
class Attendance < ActiveRecord::Base
belongs_to :user
class Student < User
has_one :student_detail
default_scope includes(:student_detail)
class StudentDetail < ActiveRecord::Base
belongs_to :school
class Staff < User
has_one :staff_detail
default_scope includes(:staff_detail)
class StaffDetail < ActiveRecord::Base
那么我认为:
Event.includes(:attendances => :user).where(...)
应该为学生和教职员工预加载。
最简单的方法是将 has_one
关联向下移动到用户。由于只有 Staff
条记录会有 staff_details
,预加载将正常工作。
class User < ActiveRecord::Base
has_one :staff_detail
has_one :student_detail
end
class Staff < User; end
class Student < User; end
但这并不理想。要进一步自定义预加载,您可以使用 Rails 中的 Preloader
class。首先,加载所有 不包含 的记录,然后遍历它们并预加载您需要的关联:
events = Event.includes(:attendances => :user)
users = events.users.flatten
users.group_by(&:class).each do |klass, records|
associations = {
Staff: [:staff_detail],
Student: [:student_detail]
}.fetch(klass, [])
ActiveRecord::Associations::Preloader.new(records, associations).run
end
注意这个APIchanged in Rails 4. In versions 3 and earlier, you just used the preload_associations
方法。
前段时间我写了一篇 blog post about this same problem,其中包括一些其他巧妙的技巧(例如指定您获得正确的行为)。
您可以使用命名作用域让您的生活更轻松一些。
class Event < ActiveRecord::Base
has_many :attendances
scope :for_students, -> { includes(:attendances => { :users => :student_detail }).where('users.type = ?', 'Student') }
scope :for_staff, -> { includes(:attendances => { :users => :staff_detail }).where('users.type = ?', 'Staff') }
end
那你就可以Event.for_students