我有以下型号
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用户表中使用它,因为必须使用类似每个表模式的具体类
我可以轻松地做到这一点
Event.includes(:attendances => :user).where(...)
但我希望能够根据用户类型进行包含 e.g。
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
这不太理想。 有人有任何提示吗?解决这个问题的方法。
答案 0 :(得分:7)
最简单的方法是将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
类。首先,加载所有记录,不用任何包含,然后迭代它们并预加载所需的关联:
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
请注意,此API changed in Rails 4。在版本3及更早版本中,您只使用了preload_associations
方法。
前段时间我写了一个blog post about this same problem,其中包含了一些其他巧妙的技巧(例如说明你得到了正确的行为)。
答案 1 :(得分:2)
如何将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(...)
应该为学生和教职员工加急。
答案 2 :(得分:1)
您可以使用命名范围让您的生活更轻松。
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