6

设置

对于这个问题,我将使用以下三个类:

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范围 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_typesthrough 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
于 2012-10-23T18:52:01.227 回答
1

我发现一个简单的解决方案是,您可以将 Planet 类中的联接更改为

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

这将为连接创建一个 SQL 字符串,该字符串将始终包含正确的表名,因此无论您是直接调用范围还是在合并中使用它,都应该始终有效。它看起来不是那么漂亮,可能有点小技巧,但它只是多一点代码,没有必要改变你的关联。

于 2016-09-28T08:47:18.970 回答