2012-06-20 9 views
6

Ricevo uno strano comportamento durante il recupero delle raccolte da un'associazione has_many con le guide 3 quando si utilizza STI. Ho:Rails Associazione STI con sottoclassi

class Branch < ActiveRecord::Base 
    has_many :employees, class_name: 'User::Employee' 
    has_many :admins, class_name: 'User::BranchAdmin' 
end 

class User < ActiveRecord::Base 
end 

class User::Employee < User 
    belongs_to :branch 
end 

class User::BranchAdmin < User::Employee 
end 

il comportamento desiderato è che branch.employees restituisce tutti i dipendenti, compresi gli amministratori di filiale. Gli amministratori filiali sembrano solo per essere 'caricato' in questa collezione, quando sono stati accede da branch.admins, questo viene emesso dalla console:

Branch.first.employees.count 
=> 2 

Branch.first.admins.count 
=> 1 

Branch.first.employees.count 
=> 3 

Questo può essere visto in SQL generato, per la prima volta:

SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee') AND "users"."branch_id" = 1 

e la seconda volta:

SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee', 'User::BranchAdmin') AND "users"."branch_id" = 1 

ho potuto risolvere questo problema semplicemente specificando:

0.123.
class Branch < ActiveRecord::Base 
    has_many :employees, class_name: 'User' 
    has_many :admins, class_name: 'User::BranchAdmin' 
end 

dal momento che tutti possono trovare dal loro branch_id ma questo crea problemi nel controller, se voglio fare branch.employees.build allora la classe imposterà User e devo incidere la colonna tipo da qualche parte. Ho risolto questo per ora con:

has_many :employees, class_name: 'User::Employee', 
    finder_sql: Proc.new{ 
     %Q(SELECT users.* FROM users WHERE users.type IN   ('User::Employee','User::BranchAdmin') AND users.branch_id = #{id}) 
    }, 
    counter_sql: Proc.new{ 
     %Q(SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee', 'User::BranchAdmin') AND "users"."branch_id" = #{id}) 
    } 

ma vorrei davvero evitare questo se possibile. Qualcuno, qualche idea?

EDIT:

Il finder_sql e counter_sql non hanno realmente risolto per me perché sembra che le associazioni di genitori non usano questo e così organisation.employees che has_many :employees, through: :branches sarà di nuovo includere solo la classe User::Employee nella selezione.

risposta

17

Fondamentalmente, il problema esiste solo nell'ambiente di sviluppo in cui le classi vengono caricate secondo necessità. (Nella produzione, le classi vengono caricate e tenuti a disposizione.)

Il problema viene in causa l'interprete non avendo ancora visto che Admins sono un tipo di Employee prima volta che si esegue il Employee.find, ecc chiamata.

(Si noti che in seguito utilizza IN ('User::Employee', 'User::BranchAdmin'))

Ciò accade con ogni uso di classi di modelli che sono più di un livello profondo, ma solo in dev-mode.

Le sottoclassi caricano sempre automaticamente la gerarchia principale. Le classi base non caricano automaticamente le loro gerarchie figlio.

Hack-fix:

È possibile forzare il comportamento corretto in dev-mode richiedendo esplicitamente tutte le classi figlie dal file rb classe di base.

+2

Questa è una bella cattura, grazie. La struttura del modello è stata effettivamente modificata in ogni caso, quindi il problema è scomparso, ma non credo che l'avrei considerato un effetto dell'ambiente! –

2

È possibile utilizzare :conditions?

class Branch < ActiveRecord::Base 
    has_many :employees, class_name: 'User::Employee', :conditions => {:type => "User::Employee"} 
    has_many :admins, class_name: 'User::BranchAdmin', :conditions => {:type => "User::BranchAdmin"} 
end 

Questo sarebbe il mio metodo preferito. Un altro modo per farlo potrebbe essere quello di aggiungere un ambito di default ai modelli polimorfici.

class User::BranchAdmin < User::Employee 
    default_scope where("type = ?", name) 
end 
+0

Ho provato a utilizzare le condizioni ma ho avuto problemi anche con questo. La struttura dell'app è ora cambiata, quindi non ho dovuto preoccuparmi di questo, questo potrebbe essere stato risolto in rotaie 3.2.7. –