web-dev-qa-db-ja.com

Rails has_many関係でデフォルトでスコープを使用する

次のクラスがあるとしましょう

_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のスコープは_life_supporting_およびSolarSystem _has_many :planets_です。 has_many関係を定義して、関連するすべてのplanetsに対して_solar_system_を要求すると、_life_supporting_スコープが自動的に適用されるように定義したいと思います。基本的に、_solar_system.planets == solar_system.planets.life_supporting_が欲しいです。

必要条件

  • notPlanetの_scope :life_supporting_を変更したい

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

  • また、SolarSystemに追加する必要がないため、重複を防ぎたい

    _has_many :planets, :conditions => ['distance_from_Sun > ?', 5], :order => 'diameter ASC'_

ゴール

次のようなものが欲しい

_has_many :planets, :with_scope => :life_supporting_

編集:回避策

@phoetが言ったように、ActiveRecordを使用してデフォルトのスコープを達成することは不可能かもしれません。ただし、2つの潜在的な回避策が見つかりました。両方とも重複を防ぎます。最初のものは長い間、明らかな読みやすさと透明性を維持し、2番目のものは出力が明示的なヘルパー型メソッドです。

_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
_

よりクリーンな別の解決策は、単に次のメソッドをSolarSystemに追加することです

_def life_supporting_planets
  planets.life_supporting
end
_

また、_solar_system.life_supporting_planets_を使用する場合はいつでも_solar_system.planets_を使用します。

どちらも質問に答えないので、他の誰かがこの状況に遭遇した場合の回避策としてここにそれらを配置します。

73
Aaron

Rails 4、Associationsには、scopeに適用されるラムダを受け入れるオプションのRelationパラメーターがあります(cf. 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

Rails 3、where_values回避策は、where_values_hash条件が複数のwhereまたはハッシュ(ここでは当てはまらない)によって定義される場合、より良いスコープを処理します。

has_many :planets, conditions: Planet.life_supporting.where_values_hash
114
gregclermont

Rails 5では、次のコードは正常に動作します...

  class Order 
    scope :paid, -> { where status: %w[paid refunded] }
  end 

  class Store 
    has_many :paid_orders, -> { paid }, class_name: 'Order'
  end 
3

activeRecordを詳しく調べたところ、現在のhas_manyの実装でこれを達成できるかどうかはわかりません。ブロックを:conditionsに渡すことができますが、これは条件のハッシュを返すことに制限されており、いかなる種類のアレルのものも返しません。

あなたが望むもの(あなたがしようとしていると思うこと)を達成するための本当にシンプルで透明な方法は、実行時にスコープを適用することです:

  # foo.rb
  def bars
    super.baz
  end

これはあなたが求めているものとはほど遠いですが、うまくいくかもしれません;)

1
phoet