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
Planet
tiene un alcance life_supporting
y SolarSystem
has_many :planets
. Me gustaría definir mi relación has_many para que cuando pregunte solar_system
por todos los asociados planets
, el life_supporting
alcance se aplique automáticamente. Esencialmente, me gustaría solar_system.planets == solar_system.planets.life_supporting
.
Requisitos
Yo no quiero cambiar
scope :life_supporting
dePlanet
adefault_scope where('distance_from_sun > ?', 5).order('diameter ASC')
También me gustaría evitar la duplicación al no tener que agregar a
SolarSystem
has_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_planets
donde 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_values
podrí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_ast
lugar dewhere_values
owhere_values_hash
como había usado AREL en el alcance del otro modelo. ¡Funcionó de maravilla! +1Respuestas:
En Rails 4,
Associations
tenga unscope
pará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') } end
En Rails 3, la
where_values
solución a veces se puede mejorar utilizandowhere_values_hash
que maneja mejores ámbitos donde las condiciones están definidas por múltipleswhere
o por un hash (no es el caso aquí).has_many :planets, conditions: Planet.life_supporting.where_values_hash
fuente
has_many :planets, conditions: Planet.life_supporting.where_values_hash
para hacer cumplir el alcance. Esto también es dorado para una carga ansiosa.where_values_hash
no 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_hash
que 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' end
fuente
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,:conditions
pero 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 end
esto está lejos de lo que está pidiendo, pero podría funcionar;)
fuente
has_many
declaración, ya que está más clara.