93 votes

Utiliser une étendue par défaut sur une relation Rails has_many

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 mon has_many relation de sorte que lorsque je demande un solar_system pour tous les associés planets, l' life_supporting champ est automatiquement appliquée. Essentiellement, je voudrais solar_system.planets == solar_system.planets.life_supporting.

Exigences

  • Je n'ai pas envie de changer de scope :life_supporting en Planet de

    default_scope where('distance_from_sun > ?', 5).order('diameter ASC')

  • Je tiens également à éviter la duplication de ne pas avoir à ajouter d' SolarSystem

    has_many :planets, :conditions => ['distance_from_sun > ?', 5], :order => 'diameter ASC'

Objectif

J'aimerais avoir quelque chose comme

has_many :planets, :with_scope => :life_supporting

Edit: Solutions

Comme @phoet dit, il peut ne pas être possible de parvenir à un défaut portée à l'aide d'ActiveRecord. Cependant, j'ai trouvé deux solutions. Les deux éviter les doublons. La première, alors que pendant longtemps, maintient évident de lisibilité et de transparence, et le second est un helper de type méthode qui est 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 qui est beaucoup plus propre est tout simplement ajouter la méthode suivante pour SolarSystem

def life_supporting_planets
  planets.life_supporting
end

et pour utiliser solar_system.life_supporting_planets où vous l'auriez solar_system.planets.

Ni de réponses à la question je viens donc de mettre ici que les solutions quelqu'un d'autre devrait rencontrer cette situation.

147voto

Grégoire Clermont Points 396

Dans les Rails 4, Associations ont une option scope paramètre qui accepte un lambda qui est appliquée à l' Relation (cf. le 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, l' where_values solution de contournement peut parfois être améliorée par l'utilisation d' where_values_hash qui gère mieux étendues où les conditions sont définies par plusieurs where ou par une table de hachage (pas le cas ici).

has_many :planets, conditions: Planet.where_values_hash

1voto

phoet Points 12124

j'ai juste eu une plongée dans les ActiveRecord et il ne ressemble pas à si cela peut être réalisé avec l'implémentation actuelle de l' has_many. vous pouvez passer d'un bloc à l' :conditions mais il est limité à retour d'un hachage de conditions, pas n'importe quel type de arel choses.

l'une, très simple et transparent à 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 il est possible que ça fonctionne ;)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X