【问题标题】:Rails 3: Building a set of filters on an index view using scopesRails 3:使用范围在索引视图上构建一组过滤器
【发布时间】:2012-08-02 23:15:10
【问题描述】:

我有一个模型的索引视图,我想通过模型属性的某种组合进行过滤。

例如,我有一个账单模型(不是鸭子上的那种,你必须支付的那种),我可能会根据收款人和/或状态进行过滤。

模型对每个单独的属性都有一个范围,例如

scope :bill_status, lambda {|status| where("status = ?", status}
scope :bill_payee, lambda {|payee| where("payee_id = ?", payee.id}

视图允许用户选择零个或多个选项——如果没有选择一个选项,则表示“不按此过滤”。

在控制器中,我可以做一些像这样令人讨厌的事情:

def index
  status = params[:bill][:status]
  payee = params[:bill][:payee]
  if status.present? and payee.present?
    # chain scopes
    @bills = Bill.bill_status(status).bill_payee(payee)
  elsif status.present?
    @bills = Bill.bill_status(status)
  elsif payee.present?
    @bills = Bill.bill_payee(payee)
  else
    @bills = Bill.all
  end

  # rest of controller action
end

虽然这可行,但它既不美观也不易于扩展——添加第三个过滤器意味着我现在有更多的可能性。我追求美丽和纯洁。

假设我的作用域都是可链接的,似乎我应该能够做类似的事情

def index
  @bills = Bill.all
  @bills = @bills.bill_status(params[:bill][:status]) if params[:bill][:status].present?
  @bills = @bills.bill_payee(params[:bill][:payee]) if params[:bill][:payee].present?

  # rest of controller code
end

'除了它不起作用,因为Bill.all 是一个数组。另外,这并不好玩,因为Bill.all 执行查询,多亏了 AREL 的魔法,我只想运行一次。我应该只定义一个像all_bills 这样的范围吗(没有条件?)——我猜那将是一个ActiveRecord::Relation...

有没有更优雅地解决这个问题的模式?每当我有代码来做一件依赖于模型、视图和控制器的事情时,我都会觉得我可能做错了什么。或者好像比我更聪明、更努力的人已经解决了它:-)

额外问题:我希望这一切都能与我选择的分页器一起使用,这是最优秀的 Kaminari 宝石。

欢迎所有想法和想法。

【问题讨论】:

  • 你试过has_scope吗?我刚刚在另一个问题上推荐了这个stackoverflow.com/a/11820424/167614
  • 嗨@beerlington has_scope gem 看起来很有趣,它来自 Platformatec 团队的血统是无可挑剔的。我偏爱来自@mu-is-too-short 的另一个线程的答案,它解决了我的方法的一个基本问题——而不是从@bills = Bill.all 开始,我可以从@bills = Bill 开始(它提供了基础AREL 范围链接),然后在最后使用@bills = @bills.all 运行整个过程。两种解决方案都很棒。现在确保他们都与 Kaminari 一起工作以进行分页。谢谢。
  • has_scope 绝对适用于 will_paginate,所以如果它也不适用于 Kaminari,我会感到惊讶。

标签: ruby-on-rails-3 activerecord arel


【解决方案1】:

我会这样做:

proxy = Bill.scoped
if status.present?
  proxy = proxy.bill_status(status)
end
if payee.present?
  proxy = proxy.bill_payee(payee)
end

@bills = proxy

你甚至可以做一些元编程:

@bills = [:status, :payee, ...].inject(Bill.scoped) do |proxy, param|
  val = params[:bill][param]
  val.present ? proxy.send("bill_#{param}", val) : proxy
end

【讨论】:

  • 非常好:-)。就像元编程解决方案一样——有点偏局部,而且很神奇!
【解决方案2】:

在寻找看似常见问题的解决方案时,我查看了 Ryan Bates 的RailsCast and found an episode from 3 days ago on Ransack。 Ransack 是用于表单搜索和列排序的非常酷的宝石,我认为这就是我要走的路。

在这里感谢您的回答——我很高兴从那些花时间和精力回答问题的人那里学到了一些很棒的技巧。

【讨论】:

  • 我问过之后的一个更新——我用过 Ransack 0.7,它是一个很棒的软件,对文档有点轻,但整体非常优雅——视图不需要特殊标记,只是按照约定命名对于搜索字段(非常 Railsy),控制器中多出一行,模型中没有任何内容。
猜你喜欢
  • 1970-01-01
  • 2020-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
相关资源
最近更新 更多