繁体   English   中英

Rails 3将范围与连接合并

[英]Rails 3 merging scopes with joins

设定

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

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_planetSolarSystem ,并且想避免重复的代码和条件。 有没有办法合并这些范围,就像我试图做但却缺少一块? 如果没有,我可以用什么其他技术来实现这些目标?

显然,此时您只能合并不涉及连接的简单构造。 如果您将模型修改为如下所示,这是一种可能的解决方法:

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

**更新**

为了记录, 提出了一个关于这种行为的错误 - 希望很快就会修复......

您正在重用Planet.like_earth范围内的条件,该范围加入了planet_type 合并这些条件后,将在SolarSystem上调用planet_type关联,而SolarSystem不存在。

SolarSystem通过planets有许多planet_types ,但这仍然不是正确的关联名称,因为它是多元化的。 您可以将以下内容添加到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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM