ruby-on-rails Ransack:如何使用现有的范围?

cbjzeqam  于 2023-05-30  发布在  Ruby
关注(0)|答案(3)|浏览(111)

要将Rails 2应用程序转换到Rails 3,我必须替换gem searchlogic。现在,使用Rails 3.2.8和gem Ransack,我想构建一个使用现有作用域的搜索表单。示例:

class Post < ActiveRecord::Base
  scope :year, lambda { |year| 
    where("posts.date BETWEEN '#{year}-01-01' AND '#{year}-12-31'") 
  }
end

据我所知,这可以通过定义自定义ransacker来实现。遗憾的是,我没有找到任何关于这方面的文件。我在Post类中尝试了这个方法:

ransacker :year, 
          :formatter => proc {|v| 
            year(v)
          }

但这不起作用:

Post.ransack(:year_eq => 2012).result.to_sql
=> TypeError: Cannot visit ActiveRecord::Relation

我尝试了ransacker声明的一些变体,但它们都不起作用。我需要帮助...

**更新:**以上范围仅为示例。我在寻找一种方法来使用Ransack中的每一个现有范围。在Ransack的前身MetaSearch中,有一个名为search_methods的功能用于使用范围。Ransack还没有开箱即用的no support for this

p3rjfoxz

p3rjfoxz1#

ransack在合并https://github.com/activerecord-hackery/ransack/pull/390后支持它。你应该声明ransackable_scopes方法来添加搜索可见的作用域。
来自手册
继续上一节,按作用域搜索需要在模型类上定义一个ransackable_scopes白名单。白名单应该是一个符号数组。默认情况下,所有类方法(例如scopes)被忽略。作用域将被应用于匹配真值,或者如果作用域接受一个值,则应用于给定值:

class Employee < ActiveRecord::Base
  scope :activated, ->(boolean = true) { where(active: boolean) }
  scope :salary_gt, ->(amount) { where('salary > ?', amount) }

  # Scopes are just syntactical sugar for class methods, which may also be used:

  def self.hired_since(date)
    where('start_date >= ?', date)
  end

  private

  def self.ransackable_scopes(auth_object = nil)
    if auth_object.try(:admin?)
      # allow admin users access to all three methods
      %i(activated hired_since salary_gt)
    else
      # allow other users to search on `activated` and `hired_since` only
      %i(activated hired_since)
    end
  end
end

Employee.ransack({ activated: true, hired_since: '2013-01-01' })

Employee.ransack({ salary_gt: 100_000 }, { auth_object: current_user })
ao218c7q

ao218c7q2#

Ransack让你为此创建自定义 predicate ,不幸的是文档留下了改进的空间,但是结帐:https://github.com/ernie/ransack/wiki/Custom-Predicates
另外,我相信你试图解决的问题是在他们的问题跟踪。有一个很好的讨论正在进行:https://github.com/ernie/ransack/issues/34

68bkxrlz

68bkxrlz3#

我写了一个名为siphon的gem,它可以帮助你将参数转换为activerelection作用域。将其与ransack相结合可以实现这一点。
你可以read full explanation here。同时,这是它的要点

视图

= form_for @product_search, url: "/admin/products", method: 'GET' do |f|
  = f.label "has_orders"
  = f.select :has_orders, [true, false], include_blank: true
  -#
  -# And the ransack part is right here... 
  -#
  = f.fields_for @product_search.q, as: :q do |ransack|
    = ransack.select :category_id_eq, Category.grouped_options

好了,现在`params[:product_search]`持有范围,`params[:product_search][:q]`拥有ransack goodness。我们现在需要找到一种方法,将数据分发到表单对象。因此,首先让ProductSearch在控制器中吞下它:

### 控制器

products_controller.rb

def index
@product_search = ProductSearch.new(params[:product_search])
@products ||= @product_formobject.result.page(params[:page])
end


### Form对象

product_search.rb

class ProductSearch
include Virtus.model
include ActiveModel::Model

These are Product.scopes for the siphon part

attribute :has_orders, Boolean
attribute :sort_by, String

The q attribute is holding the ransack object

attr_accessor :q

def initialize(params = {})
@params = params || {}
super
@q = Product.search( @params.fetch("q") { Hash.new } )
end

siphon takes self since its the formobject

def siphoned
Siphon::Base.new(Product.scoped).scope( self )
end

and here we merge everything

def result
Product.scoped.merge(q.result).merge(siphoned)
end
end

相关问题