2015-01-06 12 views
5

ho il seguente modelloattivo record include con 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 

Lo StudentDetail e StaffDetails hanno ulteriori informazioni, sto cercando di evitare di avere tutto in una sola tabella utente STI a causa di dover lavorare con qualcosa di simile a classe concreta per modello tavolo

posso farlo abbastanza facilmente

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

ma voglio essere un da includere a seconda del tipo di utente ad es.

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

Ciò non riuscirà poiché alcuni utenti sono oggetti Staff.

mi rendo conto rotaie non sostenere questo, fuori dalla scatola, ma chiunque avere trucchi per ottenere questo al lavoro

soluzione migliore in questo momento sarebbe stato diviso utente sulla frequenza a studenti e del personale cioè

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

che non è l'ideale. Qualcuno ha qualche consiglio? modo per risolvere questo.

risposta

6

Il modo più semplice è spostare le associazioni has_one in basso all'utente. Poiché solo i record Staff avranno staff_details, il preloading funzionerà.

class User < ActiveRecord::Base 
    has_one :staff_detail 
    has_one :student_detail 
end 

class Staff < User; end 
class Student < User; end 

Questo non è l'ideale però. Per personalizzare ulteriormente il pre-caricamento, è possibile utilizzare la classe Preloader in Rails. In primo luogo, caricare tutti i record senza alcun comprende, quindi scorrere su di loro e precaricare le associazioni cui avete bisogno:

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 

Si noti che questa API changed in Rails 4. Nelle versioni 3 e precedenti, hai appena utilizzato il metodo preload_associations.

Un po 'di tempo fa ho scritto uno blog post about this same problem che include un paio di altri trucchi accurati (come specificare che si ottiene un comportamento corretto).

+2

La sintassi cambiato di nuovo, per Rails 4.1 I credere. La nuova sintassi è 'ActiveRecord :: Associations :: Preloader.new.preload (records, associations)'.Vedi [commit] (https://github.com/rails/rails/commit/6e5a2cb9519aab568ea0cfea2f42364de8ccf655) per i dettagli. – Leito

2

Come circa mettere il includes sui modelli STI come 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 

allora penso questo:

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

dovrebbe caricarsi desiderosi sia per gli studenti e il personale.

1

È possibile utilizzare gli ambiti denominati per semplificare la vita.

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 

allora si può solo fare Event.for_students