对于这个问题,我将使用以下三个类:
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)...
和SolarSystem没有planet_type
关联。我想使用like_earth
上的Planet
范围,has_earthlike_planet
上的SolarSystem
,并希望避免重复代码和条件。有没有办法合并这些范围,就像我试图做但却缺少一块?如果没有,我可以用什么其他技术来实现这些目标?
答案 0 :(得分: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
**更新**
对于记录,bug was filed关于此行为 - 希望很快就会修复......
答案 1 :(得分:1)
您正在重复使用加入Planet.like_earth
的范围planet_type
中的条件。合并这些条件后,将在planet_type
上调用SolarSystem
关联,该关联不存在。
SolarSystem
有许多planet_types
到planets
,但这仍然不是正确的关联名称,因为它是多元化的。您可以将以下内容添加到SolarSystem
类,以设置planet_type
关联,该关联只是planet_types
的别名。您不能使用Ruby alias
,因为AREL反映了关联宏,并且不查询模型是否响应该名称的方法:
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
答案 2 :(得分:1)
我发现一个简单的解决方案是您可以将Planet类中的联接更改为
joins(Planet.joins(:planet_type).join_sql)
这将为连接创建一个SQL字符串,它始终包含正确的表名,因此无论您是直接调用范围还是在合并中使用它,都应该始终有效。它不是那么漂亮,可能有点像黑客,但它只是一点点代码,没有必要改变你的关联。