创建一个涉及3个表的Rails 3命名范围

cra*_*410 1 ruby-on-rails ruby-on-rails-3

使用Ruby 1.9.2在Mac OS X上运行Rails 3.0.7

我有三个模型,后面有表格(sqlite3表示dev,postgres表示prod).它们如下(验证和其他不相关的行被截断):

class ServiceProvider < ActiveRecord::Base
  has_many :services
  has_many :advertisements, :through => :services
  scope :active, where("service_providers.active = ?", true)
end

class Service < ActiveRecord::Base
  belongs_to :service_provider
  has_many :advertisements
  scope :active, joins(:service_provider).where("services.active = ?", true) & ServiceProvider.active
end

class Advertisement < ActiveRecord::Base
  belongs_to :service
  scope :ranked, where("advertisements.rank > 0")
  scope :current, where("start_date <= ? AND end_date >= ?", Date.today, Date.today)
  scope :service_active, joins(:service) & Service.active
  scope :active, ranked.current.service_active
end
Run Code Online (Sandbox Code Playgroud)

如您所见,每个服务提供商都有许多服务,每个服务都可以有很多广告.广告表中包含service_id外键,services表中包含service_provider_id外键.所有相当标准的多对一关系.可以通过"活动"标志使服务提供者处于非活动状态,就像个别服务一样.

我想要做的是在广告模型上创建一个命名范围,以便为我提供所有广告的列表,这些广告是排名和当前以及谁的父服务是活动的,反过来谁的service_provider是活动的.换句话说,如果服务变为非活动状态,那么指向它的所有广告应该变为非活动状态,如果服务提供者变为非活动状态,则所有服务,以及这些服务下的所有广告应该变为非活动状态.

我已经开始创建上面所需的命名范围,它在Service.active和ServiceProvider.active级别按预期工作,但如果我输入Advertisement.active,我会收到一个错误:

ActiveRecord::ConfigurationError: Association named 'service_provider' was not found; perhaps you misspelled it? 
Run Code Online (Sandbox Code Playgroud)

我认为这是因为我没有将service_provider_id作为广告商表中的外键字段,但我认为我不需要它,因为它应该通过服务表上的service_provider_id字段访问service_provider.感觉我需要在我的广告模型中使用belongs_to:service_provider,:through =>:service子句,但据我所知这是不可能的.我已经尝试过各种各样的方法来实现这个目标,但我正在努力取得进展.

任何人都可以告诉我一个很好的方式来实现上述,理想情况下,不改变我的表结构?我需要它非常有效,因为此查询将针对我网站上的每个页面加载运行(每页上的侧边栏广告).

非常感谢,克雷格.

Kyl*_*yle 7

我认为以下方法可行:

class Advertisement
    scope :service_active, joins(:service => :service_provider)
                           .where(:services => { :active => true })
                           .where(:service_providers => { :active => true })
end
Run Code Online (Sandbox Code Playgroud)

然后你应该可以打电话

Advertisement.ranked.current.service_active
Run Code Online (Sandbox Code Playgroud)

并获得预期的结果.