Active recording includes STIs

I have the following model

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 and StaffDetails have additional information, I try to avoid all of it in the same user STI table due to the need to work with something similar to a specific class for a table template

I can do it easy enough

 Event.includes(:attendances => :user).where(...) 

but I want to be able to include depending on the type of user for example.

 Event.includes(attendances: {:user => :student_details }) 

This will fail because some of the users are Staff objects.

I understand that the rails will not support this out of the box, but who has any tricks to make it work.

The best solution right now will be shared by the user when visiting the student and staff that is.

 class Attendance < ActiveRecord::Base belongs_to :student, -> {includes(:staff_detail) } belongs_to :staff, -> {includes(:student_detail) } #belong_to :user 

which is not ideal. Does anyone have any clues? way to solve this problem.

+5
source share
3 answers

The easiest way is to simply move the has_one associations to the user. Since only Staff records will have staff_details , preloading will work.

 class User < ActiveRecord::Base has_one :staff_detail has_one :student_detail end class Staff < User; end class Student < User; end 

This is not perfect. To further customize the preload, you can use the Preloader class in Rails. First, download all the records without any inclusion, then iterate over them and preload the necessary associations:

 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 

Note that this API has been changed in Rails 4 . In versions 3 and earlier, you used the preload_associations method.

A back I wrote a message

+6
source

How to place includes on STI models as default_scope ?

 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 

Then I think the following:

 Event.includes(:attendances => :user).where(...) 

High load required for students and staff.

+2
source

You can simply use named fields to make your life a little easier.

 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 

Then you can just do Event.for_students

+1
source

Source: https://habr.com/ru/post/1210436/


All Articles