是否可以通过命名范围初始化实例变量?我想在初始搜索后记住我的搜索条件。请参阅以下内容:
class Foo < ActiveRecord::Base
attr_accessor :search
named_scope :bar, lambda {|search|
# Do something here to set the @search variable to search
{
:conditions => [
"name LIKE :search OR description LIKE :search",
{:search => "%#{search}%"} ]
}
}
def match
if name.match(@search)
:name
elsif description.match(@search)
:description
end
end
end
或者,我可以创建一个调用命名范围的类方法,然后遍历结果以在每个上设置实例变量,但在这种情况下我失去了命名的范围可链接性。
答案 0 :(得分:1)
非常感谢您提供有关如何做的最新文章。 这个猴子补丁仍然可以在Rails 5上使用。
当将范围与“ where()”条件链接时,我刚刚遇到了上下文没有转发的情况。
为此,我向Association的CollectionProxy添加了另一个补丁,该补丁似乎适用于我当前的情况。
也许对您或其他人有用:
module ActiveRecord
module Associations
class CollectionProxy
alias_method :original_scope, :scope
def scope
return @scope if @scope
original_scope.tap { |new_scope|
new_scope.scope_context = scope_context
}
end
end
end
end
答案 1 :(得分:0)
命名范围不会实例化任何单个记录,直到它们被迭代。正如你所说的那样,他们可能会被更多的范围链接。
我看到两种可能的解决方案。一个依赖于数据库来填充具有匹配值的假列。使用查询的特殊:select
选项(或rails 3中.select()
的特殊参数)应该很简单
另一种是依靠控制器。这更容易实现,但您必须确保ruby中的“匹配”与数据库中的“LIKE”相同。我说的是整理,unicode规范化形式等。最有可能至少有一种情况,它们在两个引擎中表现不同。
答案 2 :(得分:0)
我一直疯狂地试图在过去一周内完成同样的问题,并最终成功地解决了这个问题。由于我在遇到麻烦时发现了这个问题,我想我会在这里分享答案,以防万一其他人发现自己遇到了类似的问题。
作为rewritten stated in his answer,您无法在范围内设置模型的实例变量,因为还没有模型实例。范围仅用于构建将在尝试迭代范围结果时将评估的sql表达式。但是,存在的是ActiveRecord :: Relation对象。这是包含范围细节的内容。通过将要保留的变量放在该对象上,以后它们仍然可以访问。
那么,如何将变量放到Relation对象上?猴子补丁来到你的救援:
module ActiveRecord
class Relation
attr_writer :scope_context
def scope_context
@scope_context ||= {}
end
end
module SpawnMethods
alias_method :old_merge, :merge
def merge(r)
merged = old_merge(r)
merged.scope_context.deep_merge! r.scope_context
merged
end
end
end
现在,在所有范围中,您都有一个scope_context变量。 SpawnMethods#merge malarky是为了确保scope_context保持在一系列范围内(例如Foo.search(xxx).sort(xxx).paginate(xxx)
)
require 'active_support/concern'
module Searchable
extend ActiveSupport::Concern
included do
self.scope :search, Proc.new { |params|
s = scoped
s.scope_context[:searchable] = {}
s.scope_context[:searchable][:params] = parse_params params
s.scope_context[:searchable][:params].each do |param|
s = s.where generate_where_expression param
end
s
} do
def search_val col_name
param = scope_context[:searchable][:params].find do |param|
param[:col_name] == field.to_s
end
param.nil? ? '' : param[:val]
end
end
end
module ClassMethods
def parse_params params
# parse and return the params
end
def generate_where_expression param
"#{param[:col_name]} like '%#{param[:val]}%'"
end
end
end
现在,我们的控制器,模型和视图将如下所示:
class FooController < ApplicationController
def index
@foos = Foo.search(params[:search])
end
end
class Foo < ActiveRecord::Base
include Searchable
attr_accessor :name, :description
end
<p>You searched for:</p>
<table>
<tr>
<th>Name</th>
<td><%= @foos.search_val :name %>
</tr>
<tr>
<th>Description</th>
<td><%= @foos.search_val :description %>
</tr>
</table>
<p>And you got:</p>
<table>
<% @foos.each do |foo| %>
<tr> xxx </tr>
<% end %>
</table>
现在,您可能已经注意到上面提到的core_ext和Concerses目录。您的申请需要了解这些:
# Should be fairly obvious where to put this line
config.autoload_paths += Dir[Rails.root.join('app', 'concerns', '{**}')]
Dir[File.join(Rails.root, "lib", "core_ext", "*.rb")].each {|l| require l }
不要忘记重新启动服务器而离开(假设我没有忘记包含任何内容)。
哦,我已经用Rails 3.2.13和Ruby 1.9.3提出了这个解决方案;不知道它与其他版本的表现如何。