59

我有一个 Rails 3 项目。Rails 3 带来了 Arel 和重用一个范围来构建另一个范围的能力。我想知道在定义关系(例如“has_many”)时是否有使用范围的方法。

我有具有权限列的记录。我想构建一个将我的权限列考虑在内的 default_scope,以便过滤记录(即使是通过关系访问的记录)。

目前,在 Rails 3 中,default_scope(包括我找到的补丁)不提供传递 proc 的可行方法(我需要后期变量绑定)。是否可以定义一个可以传递命名范围的 has_many ?

重用命名范围的想法如下:

Orders.scope :my_orders, lambda{where(:user_id => User.current_user.id)}
has_many :orders, :scope => Orders.my_orders

或者隐式编码关系中的命名范围如下所示:

has_many :orders, :scope => lambda{where(:user_id => User.current_user.id)}

我只是想将 default_scope 与后期绑定一起应用。我更喜欢使用 Arel 方法(如果有的话),但会使用任何可行的选项。

由于我指的是当前用户,因此我不能依赖在最后一刻未评估的条件,例如:

has_many :orders, :conditions => ["user_id = ?", User.current_user.id]
4

6 回答 6

21

我建议您看一下“命名范围已死”

作者在那里解释了 Arel 的强大功能:)

我希望它会有所帮助。

编辑#1 2014 年 3 月

正如一些评论所说,差异现在是个人品味的问题。

但是,我个人仍然建议避免将 Arel 的作用域暴露给上层(作为控制器或其他任何直接访问模型的对象),这样做需要:

  1. 创建一个范围,并通过模型中的方法公开它。该方法将是您向控制器公开的方法;
  2. 如果您从不将模型暴露给控制器(因此您在它们之上有某种服务层),那么您就可以了。反腐败层您的服务,它可以访问您的模型的范围,而无需过多担心范围的实现方式。
于 2010-03-30T16:53:10.690 回答
20

关联扩展怎么样?

class Item < ActiveRecord::Base
  has_many :orders do
    def for_user(user_id)
      where(user_id: user_id)
    end
  end
end

Item.first.orders.for_user(current_user)

更新:我想指出关联扩展相对于类方法或范围的优势是您可以访问关联代理的内部:

proxy_association.owner 返回关联所属的对象。proxy_association.reflection 返回描述关联的反射对象。proxy_association.target 返回belongs_to 或has_one 的关联对象,或has_many 或has_and_belongs_to_many 的关联对象集合。

更多细节在这里: http: //guides.rubyonrails.org/association_basics.html#association-extensions

于 2013-03-03T05:01:21.823 回答
15

我刚刚定义了类方法而不是范围,这一直很好用

def self.age0 do
  where("blah")
end
于 2010-08-18T05:36:06.077 回答
10

我使用类似的东西:

class Invoice < ActiveRecord::Base
  scope :aged_0,  lambda{ where("created_at IS NULL OR created_at < ?", Date.today + 30.days).joins(:owner) }
end 
于 2010-07-15T02:42:01.903 回答
9

您可以使用合并方法来合并来自不同模型的范围。有关更多详细信息,请在此railscast中搜索合并

于 2012-07-26T13:15:07.190 回答
3

如果你只是想获取用户的订单,为什么不直接使用关系呢?

假设当前用户可以从控制器中的 current_user 方法访问:

@my_orders = current_user.orders

这可确保仅显示用户的特定订单。您还可以使用任意嵌套连接来获取更深的资源joins

current_user.orders.joins(:level1 => { :level2 => :level3 }).where('level3s.id' => X)
于 2011-04-20T03:22:44.970 回答