6

設定

この質問では、次の3つのクラスを使用します。

class SolarSystem < ActiveRecord::Base
  has_many :planets

  scope :has_earthlike_planet, joins(:planets).merge(Planet.like_earth)
end

class Planet < ActiveRecord::Base
  belongs_to :solar_system
  belongs_to :planet_type

  scope :like_earth, joins(:planet_type).where(:planet_types => {:life => true, :gravity => 9.8})
end

class PlanetType < ActiveRecord::Base
  has_many :planets

  attr_accessible :gravity, :life
end

問題

スコープhas_earthlike_planetが機能しません。次のエラーが発生します。

ActiveRecord :: ConfigurationError:「planet_type」という名前の関連付けが見つかりませんでした。おそらくスペルを間違えましたか?

質問

これは、次のものと同等であるためであることがわかりました。

joins(:planets, :planet_type)...

太陽系には関連性がありませんplanet_typelike_earthスコープをon Planethas_earthlike_planetonで使用したいのですがSolarSystem、コードと条件の重複を避けたいと思います。私がやろうとしているが、一部が欠けているように、これらのスコープをマージする方法はありますか?そうでない場合、これらの目標を達成するために他にどのような手法を使用できますか?

4

3 に答える 3

8

どうやら、現時点では、結合を含まない単純な構成のみをマージできます。モデルを次のように変更した場合の回避策は次のとおりです。

class SolarSystem < ActiveRecord::Base
  has_many :planets
  has_many :planet_types, :through => :planets

  scope :has_earthlike_planet, joins(:planet_types).merge(PlanetType.like_earth)
end

class Planet < ActiveRecord::Base
  belongs_to :solar_system
  belongs_to :planet_type

  scope :like_earth, joins(:planet_type).merge(PlanetType.like_earth)
end

class PlanetType < ActiveRecord::Base
   has_many :planets

   attr_accessible :gravity, :life

   scope :like_earth, where(:life => true, :gravity => 9.8)
end

** アップデート **

記録のために、この動作についてバグが報告されました-うまくいけばすぐに修正されるでしょう...

于 2012-10-23T18:25:42.267 に答える
1

Planet.like_earthを結合するスコープからの条件を再利用していますplanet_type。これらの条件がマージされると、planet_typeアソシエーションが呼び出されますがSolarSystem、これは存在しません。

ASolarSystemには多くのplanet_typesスルーplanetsがありますが、複数形であるため、これはまだ正しい関連付け名ではありません。SolarSystem以下をクラスに追加してplanet_type、のエイリアスである関連付けを設定できますplanet_typesaliasただし、ARELは関連付けマクロを反映し、モデルがその名前のメソッドに応答するかどうかを照会しないため、Rubyを使用することはできません。

class SolarSystem < ActiveRecord::Base
  has_many :planets
  has_many :planet_types, :through => :planets
  has_many :planet_type, :through => :planets, :class_name => 'PlanetType'

  scope :has_earthlike_planet, joins(:planets).merge(Planet.like_earth)
end

SolarSystem.has_earthlike_planet.to_sql # => SELECT "solar_systems".* FROM "solar_systems" INNER JOIN "planets" ON "planets"."solar_system_id" = "solar_systems"."id" INNER JOIN "planets" "planet_types_solar_systems_join" ON "solar_systems"."id" = "planet_types_solar_systems_join"."solar_system_id" INNER JOIN "planet_types" ON "planet_types"."id" = "planet_types_solar_systems_join"."planet_type_id" WHERE "planet_types"."life" = 't' AND "planet_types"."gravity" = 9.8
于 2012-10-23T18:52:01.227 に答える
1

私が見つけた簡単な解決策は、Planetクラスの参加を次のように変更できることです。

joins(Planet.joins(:planet_type).join_sql)

これにより、結合のSQL文字列が作成されます。この文字列には常に正しいテーブル名が含まれるため、スコープを直接呼び出すか、マージで使用するかに関係なく、常に機能するはずです。見栄えはそれほど良くなく、ちょっとしたハックかもしれませんが、コードが少し増えただけで、関連付けを変更する必要はありません。

于 2016-09-28T08:47:18.970 に答える