默认作用域可以接受 rails 中的参数吗?
Can a default scope take arguments in rails?
我想创建一个默认范围以根据当前用户过滤所有查询。是否可以将当前用户作为参数传递给 default_scope? (我知道这可以用常规范围来完成)如果不是,另一种解决方案是什么?
而不是使用 default_scope
which has a few pitfalls,您应该考虑使用带有 lambda 的命名范围。例如scope :by_user, -> (user) { where('user_id = ?', user.id) }
然后您可以在您的控制器中使用 before_filter
,以便在您需要的所有操作中轻松使用此范围。
这也是正确的方法,因为您将无法访问模型中的辅助方法。您的模型也永远不必担心会话数据。
编辑:如何在控制器内使用 before_filter
范围:
before_filter :set_object, only: [:show, :edit, :update, :destroy]
[the rest of your controller code here]
private
def set_object
@object = Object.by_user(current_user)
end
显然你会根据你的要求改变它。在这里,我们假设您只需要一个有效的 @object,具体取决于 current_user 在您的节目、编辑、更新和销毁操作中
您不能将参数传递给默认范围,但您可以让默认范围的条件引用 proxy hash,每次检索值时都会执行其过程:
module Proxies
# ProxyHash can be used when there is a need to have procs inside hashes, as it executes all procs before returning the hash
# ==== Example
# default_scope(:conditions => Proxies::ProxyHash.new(:user_id => lambda{Thread.current[:current_user].try(:id)}))
class ProxyHash < ::Hash
instance_methods.each{|m| undef_method m unless m =~ /(^__|^nil\?$|^method_missing$|^object_id$|proxy_|^respond_to\?$|^send$)/}
def [](_key)
call_hash_procs(@target, @original_hash)
ProxyHash.new(@original_hash[_key])
end
# Returns the \target of the proxy, same as +target+.
def proxy_target
@target
end
# Does the proxy or its \target respond to +symbol+?
def respond_to?(*args)
super(*args) || @target.respond_to?(*args)
end
# Returns the target of this proxy, same as +proxy_target+.
def target
@target
end
# Sets the target of this proxy to <tt>\target</tt>.
def target=(target)
@target = target
end
def send(method, *args)
if respond_to?(method)
super
else
@target.send(method, *args)
end
end
def initialize(*_find_args)
@original_hash = _find_args.extract_options!
@target = @original_hash.deep_dup
end
private
# Forwards any missing method call to the \target.
def method_missing(method, *args, &block)
if @target.respond_to?(method)
call_hash_procs(@target, @original_hash)
@target.send(method, *args, &block)
else
super
end
end
def call_hash_procs(_hash, _original_hash)
_hash.each do |_key, _value|
if _value.is_a?(Hash)
call_hash_procs(_value, _original_hash[_key]) if _original_hash.has_key?(_key)
else
_hash[_key] = _original_hash[_key].call if _original_hash[_key].is_a?(Proc)
end
end
end
end
end
然后在ApplicationController
中你可以在每个请求的begin/end处使用around_filter
到set/unsetThread.current[:current_user]
:
class ApplicationController < ActionController::Base
around_filter :set_unset_current_user
protected
def set_unset_current_user
Thread.current[:current_user] = current_user if logged_in?
yield
ensure
Thread.current[:current_user] = nil
end
end
我想创建一个默认范围以根据当前用户过滤所有查询。是否可以将当前用户作为参数传递给 default_scope? (我知道这可以用常规范围来完成)如果不是,另一种解决方案是什么?
而不是使用 default_scope
which has a few pitfalls,您应该考虑使用带有 lambda 的命名范围。例如scope :by_user, -> (user) { where('user_id = ?', user.id) }
然后您可以在您的控制器中使用 before_filter
,以便在您需要的所有操作中轻松使用此范围。
这也是正确的方法,因为您将无法访问模型中的辅助方法。您的模型也永远不必担心会话数据。
编辑:如何在控制器内使用 before_filter
范围:
before_filter :set_object, only: [:show, :edit, :update, :destroy]
[the rest of your controller code here]
private
def set_object
@object = Object.by_user(current_user)
end
显然你会根据你的要求改变它。在这里,我们假设您只需要一个有效的 @object,具体取决于 current_user 在您的节目、编辑、更新和销毁操作中
您不能将参数传递给默认范围,但您可以让默认范围的条件引用 proxy hash,每次检索值时都会执行其过程:
module Proxies
# ProxyHash can be used when there is a need to have procs inside hashes, as it executes all procs before returning the hash
# ==== Example
# default_scope(:conditions => Proxies::ProxyHash.new(:user_id => lambda{Thread.current[:current_user].try(:id)}))
class ProxyHash < ::Hash
instance_methods.each{|m| undef_method m unless m =~ /(^__|^nil\?$|^method_missing$|^object_id$|proxy_|^respond_to\?$|^send$)/}
def [](_key)
call_hash_procs(@target, @original_hash)
ProxyHash.new(@original_hash[_key])
end
# Returns the \target of the proxy, same as +target+.
def proxy_target
@target
end
# Does the proxy or its \target respond to +symbol+?
def respond_to?(*args)
super(*args) || @target.respond_to?(*args)
end
# Returns the target of this proxy, same as +proxy_target+.
def target
@target
end
# Sets the target of this proxy to <tt>\target</tt>.
def target=(target)
@target = target
end
def send(method, *args)
if respond_to?(method)
super
else
@target.send(method, *args)
end
end
def initialize(*_find_args)
@original_hash = _find_args.extract_options!
@target = @original_hash.deep_dup
end
private
# Forwards any missing method call to the \target.
def method_missing(method, *args, &block)
if @target.respond_to?(method)
call_hash_procs(@target, @original_hash)
@target.send(method, *args, &block)
else
super
end
end
def call_hash_procs(_hash, _original_hash)
_hash.each do |_key, _value|
if _value.is_a?(Hash)
call_hash_procs(_value, _original_hash[_key]) if _original_hash.has_key?(_key)
else
_hash[_key] = _original_hash[_key].call if _original_hash[_key].is_a?(Proc)
end
end
end
end
end
然后在ApplicationController
中你可以在每个请求的begin/end处使用around_filter
到set/unsetThread.current[:current_user]
:
class ApplicationController < ActionController::Base
around_filter :set_unset_current_user
protected
def set_unset_current_user
Thread.current[:current_user] = current_user if logged_in?
yield
ensure
Thread.current[:current_user] = nil
end
end