1. ホーム
  2. ruby-on-rails-3

[解決済み] Railsのhas_manyリレーションでデフォルトでスコープを使用する

2023-04-10 07:49:48

質問

次のようなクラスがあるとします。

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_supportingSolarSystem has_many :planets . 私は、has_many リレーションシップを定義したいと思います。 solar_system に問い合わせたときに、関連するすべての planets は、その life_supporting のスコープが自動的に適用されます。基本的に、私は solar_system.planets == solar_system.planets.life_supporting .

必要条件

  • 私は ではない 変更したい scope :life_supportingPlanet

    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 .

どちらも質問の答えになっていないので、誰かがこの状況に遭遇したときの回避策として、ここに載せておきます。

どのように解決するのですか?

Rails4では Associations には、オプションで scope パラメータにラムダを指定し、それを Relation に適用されるラムダを受け取ります (cf. the doc for のドキュメントを参照してください。 )

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