2012-06-20 10 views
6

Obtengo un comportamiento extraño al recuperar colecciones de una asociación has_many con rails 3 cuando uso STI. Tengo:Rails Asociación de STI con las subclases

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 

El comportamiento deseado es que branch.employees devuelve todos los empleados incluyendo los administradores de sucursales. Los administradores de las sucursales sólo parecen ser 'cargado' en virtud de esta colección cuando hayan sido visitada por branch.admins, esta es la salida de la consola:

Branch.first.employees.count 
=> 2 

Branch.first.admins.count 
=> 1 

Branch.first.employees.count 
=> 3 

Esto se puede ver en el SQL generado, la primera vez:

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

y la segunda vez:

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

pude resolver este problema simplemente especificando:

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

ya que todos ellos se encuentran de su branch_id pero esto crea problemas en el controlador si quiero hacer branch.employees.build entonces la clase será por defecto User y tengo que cortar en la columna de tipo en alguna parte. Tengo todo esto por ahora 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}) 
    } 

pero me gustaría evitar esto si es posible. Alguien, alguna idea?

EDIT:

El finder_sql y counter_sql no han resuelto realmente por mí porque parece que las asociaciones de padres no usan esto y por lo que organisation.employeeshas_many :employees, through: :branches volverán a incluir sólo la clase User::Employee en la selección.

Respuesta

17

Básicamente, el problema solo existe en el entorno de desarrollo donde las clases se cargan según sea necesario. (En la producción, las clases se cargan y estar siempre a disposición.)

El problema viene debido a la intérprete no haber visto todavía que Admins son un tipo de Employee primera vez que se ejecuta el Employee.find, etc. llamada.

(Observe que luego usa IN ('User::Employee', 'User::BranchAdmin'))

Esto sucede con todos los usos de las clases del modelo que son más de un nivel profundo, pero sólo en dev-mode.

Las subclases siempre cargan automáticamente su jerarquía principal. Las clases base no cargan automáticamente las jerarquías de sus hijos.

Hack-fix:

Puede forzar el comportamiento correcto en dev-modo, al exigir explícitamente todas sus clases hijas del archivo rb clase base.

+2

Esta es una gran captura, gracias. La estructura del modelo realmente se modificó de todos modos, así que el problema desapareció, ¡pero no creo que siquiera lo haya considerado como un efecto del medio ambiente! –

2

¿Se puede usar :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 

Este sería mi método preferido. Otra forma de hacerlo podría ser agregar un alcance predeterminado a los modelos polimórficos.

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

Intenté usar las condiciones pero también tuve problemas con esto. La estructura de la aplicación ha cambiado ahora, así que no tuve que preocuparme por esto, esto puede haberse solucionado en los carriles 3.2.7. –

Cuestiones relacionadas