【问题标题】:Reusing named_scope to define another named_scope重用 named_scope 来定义另一个 named_scope
【发布时间】:2010-03-14 20:31:55
【问题描述】:

我所看到的问题本质

有一天,如果我没记错的话,我看到了一个重用named_scope 来定义另一个named_scope 的例子。像这样的东西(不记得确切的语法,但这正是我的问题):

named_scope :billable, :conditions => ...
named_scope :billable_by_tom, :conditions => {
    :billable => true, 
    :user => User.find_by_name('Tom')
}

问题是:如果可能的话,确切的语法是什么?我找不到了,Google 也帮不上忙。

一些解释

我之所以真正想要它,是因为我使用 Searchlogic 来定义一个复杂的搜索,这可能会导致如下表达式:

Card.user_group_managers_salary_greater_than(100)

但是太长了,不能到处放。因为,据我所知,Searchlogic 只是动态定义了 named_scopes,所以我想在 Card 类上设置一个 named_scope,如下所示:

named_scope from_big_guys, { user_group_managers_salary_greater_than(100) }

- 这是我将在我的 named_scope 中使用那个长 Searchlogic 方法的地方。但是,再一次,语法是什么?想不通。

简历

那么,named_scope 嵌套(我确实不是意味着链接)真的可能吗?

【问题讨论】:

    标签: ruby-on-rails activerecord named-scope searchlogic


    【解决方案1】:

    您可以使用 proxy_options 将一个 named_scope 回收到另一个:

    class Thing
      #...
      named_scope :billable_by, lambda{|user| {:conditions => {:billable_id => user.id } } }
      named_scope :billable_by_tom, lambda{ self.billable_by(User.find_by_name('Tom').id).proxy_options }
      #...
    end
    

    这样它可以与其他named_scopes链接。

    我在我的代码中使用了它,它运行良好。

    希望对你有帮助。

    【讨论】:

    • 需要注意的是 proxy_options 仅返回最新命名范围的范围,因此不能针对另一个派生的命名范围执行此操作。
    【解决方案2】:

    请参阅此question 不久前在 SO 提出的。 lighthouse 有一个补丁可以满足您的要求。

    【讨论】:

      【解决方案3】:

      导轨 3+

      我也有同样的问题,好消息是,在过去五年中,Rails 核心团队在范围部门取得了一些进展。

      在 Rails 3+ 中,您现在可以按预期执行此操作:

      scope :billable,        where( due: true )
      scope :billable_by_tom, -> { billable.where( user: User.find_by_name('Tom') ) }
      
      Invoice.billable.to_sql         #=> "... WHERE due = 1 ..."
      Invoice.billiable_by_tom.to_sql #=> "... WHERE due = 1 AND user_id = 5 ..."
      

      仅供参考,Rails 3+ 他们已将 named_scope 重命名为 scope。我也在使用 Ruby 1.9 语法。

      奖金回合:通用范围。

      如果除了“Tom”之外还有多个“可计费”的人,那么创建一个接受传递到块中的名称参数的通用范围可能很有用:

      scope :billable_by, lambda { |name| billable.where( user: User.find_by_name( name ) ) }
      

      然后你可以直接调用它:

      Invoice.billable_by( "Tom" ).to_sql #=> "... WHERE due = 1 AND user_id = 5 ..."
      

      顺便说一句,你可以看到我在奖金回合中使用了旧的lambda 语法。那是因为我认为当您将参数传递给它时,新语法看起来很糟糕:->( name ) { ... }

      【讨论】:

      • 至少从 3.2 开始有一个聪明的解决方案:scope :optional, ->() {where(option: true)}scope :accepted, ->() {where(accepted: true)}scope :optional_and_accepted, ->() { self.optional.merge(self.accepted) }
      • @user2481743 不确定这有多聪明(或更好)。这需要两个单独的查询并合并结果,而不是一个带有组合条件的查询。
      • 好吧,绝对不会,merge 将合并范围,而不是结果。这将产生一个查询和一个可重用的可链接范围。试一试,我相信你会喜欢这个解决方案的!如果merge 的参数是一个 ActiveRecord::Relation,它会合并条件:api.rubyonrails.org/classes/ActiveRecord/… 这会产生一个查询。
      • @user2481743 哦,ActiveRecord 这样做很聪明。所以,使用你的例子,你不能直接去:scope :optional_and_accepted, ->() { self.optional.accepted } 而不必使用merge
      • 因为它只是一个示例,它可以是在任何其他 AR 模型上定义的任何其他范围,joins()。所以我想把它说清楚。你是对的,它可以缩短,但它不符合教学目的。
      【解决方案4】:

      链范围。

      一般来说,为什么不使用scope 来表示Tom 的内容,例如:

      scope :by_tom, where( user: User.find_by_name('Tom') )
      

      然后您可以通过以下方式获取那些“可由 Tom 计费”的记录:

      Record.billable.by_tom
      

      【讨论】:

      • 再想一想,你真的想把它放在一个 lambda 中,这样你就可以在那里传递任何名字,而不仅仅是“Tom”,比如:scope :by_tom, ->(name) { where( user: User.find_by_name(name) ) }
      • 我上面评论中的错字,应该是scope :by, ->(name) { where( user: User.find_by_name(name) ) },然后你可以用Record.by('Tom')调用它。
      【解决方案5】:

      您可以使用一种方法来组合一些命名范围,例如:

      
      def self.from_big_guys
        self.class.user_group_managers_salary_greater_than(100)
      end
      

      此功能是在 Rails 3 上添加的,具有新语法 (http://m.onkey.org/2010/1/22/active-record-query-interface)

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多