Digamos que tengo las siguientes clases
class SolarSystem < ActiveRecord::Base
has_many :planets
end
class Planet < ActiveRecord::Base
scope :life_supporting, where('distance_from_sun > ?', 5).order('diameter ASC')
end
Planettiene un alcance life_supportingy SolarSystem has_many :planets. Me gustaría definir mi relación has_many para que cuando pregunte solar_systempor todos los asociados planets, el life_supportingalcance se aplique automáticamente. Esencialmente, me gustaría solar_system.planets == solar_system.planets.life_supporting.
Requisitos
Yo no quiero cambiar
scope :life_supportingdePlanetadefault_scope where('distance_from_sun > ?', 5).order('diameter ASC')También me gustaría evitar la duplicación al no tener que agregar a
SolarSystemhas_many :planets, :conditions => ['distance_from_sun > ?', 5], :order => 'diameter ASC'
Objetivo
Me gustaria tener algo como
has_many :planets, :with_scope => :life_supporting
Editar: Work Arounds
Como dijo @phoet, puede que no sea posible lograr un alcance predeterminado usando ActiveRecord. Sin embargo, he encontrado dos posibles soluciones. Ambos evitan la duplicación. El primero, aunque largo, mantiene una legibilidad y transparencia obvias, y el segundo es un método de tipo auxiliar cuya salida es explícita.
class SolarSystem < ActiveRecord::Base
has_many :planets, :conditions => Planet.life_supporting.where_values,
:order => Planet.life_supporting.order_values
end
class Planet < ActiveRecord::Base
scope :life_supporting, where('distance_from_sun > ?', 5).order('diameter ASC')
end
Otra solución que es mucho más limpia es simplemente agregar el siguiente método a SolarSystem
def life_supporting_planets
planets.life_supporting
end
y para usar solar_system.life_supporting_planetsdonde quiera que lo use solar_system.planets.
Ninguno de los dos responde a la pregunta, así que los pongo aquí como solución alternativa en caso de que alguien más se encuentre con esta situación.

where_valuespodría no funcionar con condiciones hash:{:cleared => false}... da una matriz de hash que a ActiveRecord no le gusta. Como truco, tomar el primer elemento de la matriz funciona:Planet.life_supporting.where_values[0]...where_astlugar dewhere_valuesowhere_values_hashcomo había usado AREL en el alcance del otro modelo. ¡Funcionó de maravilla! +1Respuestas:
En Rails 4,
Associationstenga unscopeparámetro opcional que acepte una lambda que se aplica alRelation(consulte el documento de ActiveRecord :: Associations :: ClassMethods )class SolarSystem < ActiveRecord::Base has_many :planets, -> { life_supporting } end class Planet < ActiveRecord::Base scope :life_supporting, -> { where('distance_from_sun > ?', 5).order('diameter ASC') } endEn Rails 3, la
where_valuessolución a veces se puede mejorar utilizandowhere_values_hashque maneja mejores ámbitos donde las condiciones están definidas por múltipleswhereo por un hash (no es el caso aquí).has_many :planets, conditions: Planet.life_supporting.where_values_hashfuente
has_many :planets, conditions: Planet.life_supporting.where_values_hashpara hacer cumplir el alcance. Esto también es dorado para una carga ansiosa.where_values_hashno funciona con texto donde las cláusulas, por ejemploUser.where(name: 'joe').where_values_hash, devolverán el hash de las condiciones esperadas, mientrasUser.where('name = ?', 'Joe').where_values_hashque no. Por lo tanto, el ejemplo de los planetas probablemente fracasará.En Rails 5, el siguiente código funciona bien ...
class Order scope :paid, -> { where status: %w[paid refunded] } end class Store has_many :paid_orders, -> { paid }, class_name: 'Order' endfuente
Acabo de profundizar en ActiveRecord y no parece que esto se pueda lograr con la implementación actual de
has_many. puede pasar un bloque a,:conditionspero esto se limita a devolver un hash de condiciones, no cualquier tipo de cosas arel.una forma realmente simple y transparente de lograr lo que desea (lo que creo que está tratando de hacer) es aplicar el alcance en tiempo de ejecución:
# foo.rb def bars super.baz endesto está lejos de lo que está pidiendo, pero podría funcionar;)
fuente
has_manydeclaración, ya que está más clara.