Disons que j'ai les classes suivantes
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
a une portée life_supporting
et SolarSystem
has_many :planets
. Je voudrais définir ma relation has_many afin que lorsque je demande un solar_system
Pour tous les planets
associés, la portée life_supporting
Soit automatiquement appliquée. Essentiellement, je voudrais solar_system.planets == solar_system.planets.life_supporting
.
--- pas je veux changer scope :life_supporting
Dans Planet
en
default_scope where('distance_from_Sun > ?', 5).order('diameter ASC')
Je voudrais également éviter la duplication en évitant d'avoir à ajouter à SolarSystem
has_many :planets, :conditions => ['distance_from_Sun > ?', 5], :order => 'diameter ASC'
J'aimerais avoir quelque chose comme
has_many :planets, :with_scope => :life_supporting
Comme l'a dit @phoet, il peut ne pas être possible d'atteindre une portée par défaut en utilisant ActiveRecord. Cependant, j'ai trouvé deux solutions possibles. Les deux empêchent la duplication. La première, bien que longue, conserve une lisibilité et une transparence évidentes, et la seconde est une méthode de type assistant dont la sortie est explicite.
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
Une autre solution beaucoup plus propre consiste simplement à ajouter la méthode suivante à SolarSystem
def life_supporting_planets
planets.life_supporting
end
et d'utiliser solar_system.life_supporting_planets
partout où vous utiliseriez solar_system.planets
.
Aucun des deux ne répond à la question, je les mets simplement ici comme solution de rechange si quelqu'un d'autre devait rencontrer cette situation.
Dans Rails 4, Associations
ont un paramètre optionnel scope
qui accepte un lambda qui est appliqué au Relation
(cf. la doc pour 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
Dans Rails 3, le where_values
la solution de contournement peut parfois être améliorée en utilisant where_values_hash
qui gère de meilleures étendues où les conditions sont définies par plusieurs where
ou par un hachage (ce n'est pas le cas ici).
has_many :planets, conditions: Planet.life_supporting.where_values_hash
Dans Rails 5, le code suivant fonctionne très bien ...
class Order
scope :paid, -> { where status: %w[paid refunded] }
end
class Store
has_many :paid_orders, -> { paid }, class_name: 'Order'
end
je viens de plonger profondément dans ActiveRecord et il ne semble pas que cela puisse être réalisé avec l'implémentation actuelle de has_many
. vous pouvez passer un bloc à :conditions
mais cela se limite à renvoyer un hachage de conditions, pas n'importe quel type d'arel.
une manière vraiment simple et transparente de réaliser ce que vous voulez (ce que je pense que vous essayez de faire) est d'appliquer la portée au moment de l'exécution:
# foo.rb
def bars
super.baz
end
c'est loin de ce que vous demandez, mais ça pourrait bien marcher;)