默认情况下,在Rails has_many关系上使用范围

时间:2012-07-24 17:53:21

标签: ruby-on-rails-3 activerecord has-many

假设我有以下课程

class SolarSystem < ActiveRecord::Base
  has_many :planets
end

class Planet < ActiveRecord::Base
  scope :life_supporting, where('distance_from_sun > ?', 5).order('diameter ASC')
end

Planet的范围为life_supportingSolarSystem has_many :planets。我想定义我的has_many关系,以便当我向solar_system询问所有关联的planets时,life_supporting范围会自动应用。基本上,我想solar_system.planets == solar_system.planets.life_supporting

要求

  • 想要将scope :life_supporting中的Planet更改为

    default_scope where('distance_from_sun > ?', 5).order('diameter ASC')

  • 我还希望不必添加到SolarSystem

    来防止重复

    has_many :planets, :conditions => ['distance_from_sun > ?', 5], :order => 'diameter ASC'

目标

我想要像

这样的东西

has_many :planets, :with_scope => :life_supporting

编辑:解决方法

正如@phoet所说,使用ActiveRecord可能无法实现默认范围。但是,我发现了两个潜在的工作。两者都可以防止重复。第一个是长的,保持了明显的可读性和透明性,第二个是辅助类型的方法,其输出是显式的。

class SolarSystem < ActiveRecord::Base
  has_many :planets, :conditions => Planet.life_supporting.where_values,
    :order => Planet.life_supporting.order_values
end

class Planet < ActiveRecord::Base
  scope :life_supporting, where('distance_from_sun > ?', 5).order('diameter ASC')
end

另一个更清洁的解决方案是简单地将以下方法添加到SolarSystem

def life_supporting_planets
  planets.life_supporting
end

并在solar_system.life_supporting_planets的任何地方使用solar_system.planets

没有回答这个问题所以我只是把它们放在这里作为解决方案,如果其他人遇到这种情况。

3 个答案:

答案 0 :(得分:112)

在Rails 4中,Associations有一个可选的scope参数,该参数接受应用于Relation的lambda(参见ActiveRecord::Associations::ClassMethods的文档)

class SolarSystem < ActiveRecord::Base
  has_many :planets, -> { life_supporting }
end

class Planet < ActiveRecord::Base
  scope :life_supporting, -> { where('distance_from_sun > ?', 5).order('diameter ASC') }
end

在Rails 3中,where_values解决方法有时可以通过使用where_values_hash来处理更好的范围,其中条件由多个where或哈希定义(这里不是这种情况)

has_many :planets, conditions: Planet.life_supporting.where_values_hash

答案 1 :(得分:1)

我刚刚深入了解ActiveRecord,看起来如果可以通过has_many的当前实现来实现这一点。你可以将一个块传递给:conditions,但这仅限于返回条件的哈希,而不是任何类型的东西。

一种非常简单透明的方式来实现您想要的(我认为您正在尝试做的事情)是在运行时应用范围:

  # foo.rb
  def bars
    super.baz
  end

这远非你所要求的,但它可能会起作用;)

答案 2 :(得分:0)

在Rails 5中,以下代码可以正常工作...

  class Order 
    scope :paid, -> { where status: %w[paid refunded] }
  end 

  class Store 
    has_many :paid_orders, -> { paid }, class_name: 'Order'
  end