我用这样的动态条件定义has_many关联:
class Checkin < ActiveRecord::Base
has_many :evaluations, :conditions => proc {"evaluations.placement_id = #{self.placement_id}"}
end
假设id = 1的checkin具有placement_id = 2:
> Checkin.find(1).evaluations.to_sql
=> "SELECT \"evaluations\".* FROM \"evaluations\" WHERE \"evaluations\".\"checkin_id\" = 1 AND (evaluations.placement_id = 2)"
由于我更喜欢使用lambdas而不是proc,我正在尝试用lambda替换关联条件,但这种尝试会导致错误:
:conditions => lambda {"evaluations.placement_id = #{self.placement_id}"}
> Checkin.find(1).evaluations.to_sql
ArgumentError: wrong number of arguments (1 for 0)
通过为lambda块提供参数可以很容易地解决错误:
:conditions => lambda {|a| "evaluations.placement_id = #{self.placement_id}"}
> Checkin.find(1).evaluations.to_sql # a is nil inside of this lambda call!
=> "SELECT \"evaluations\".* FROM \"evaluations\" WHERE \"evaluations\".\"checkin_id\" = 1 AND (evaluations.placement_id = 2)"
传递参数没有区别,lambda的参数总是为零:
:conditions => lambda {|a| puts "a: #{a || 'undefined'}"; "evaluations.placement_id = #{self.placement_id}"}
Checkin.find(1).evaluations(5) # => has no effect on produced sql
a: undefined
# and here are returned evaluations
答案 0 :(得分:4)
此行为是由ActiveRecord::Associations::Association#interpolate
引起的,当Rails确定关联范围与条件时调用此行为。电话看起来像:
scope = scope.where(interpolate(condition))
以下是interpolate
方法的全文(来自3.2来源:https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/associations/association.rb):
def interpolate(sql, record = nil)
if sql.respond_to?(:to_proc)
owner.send(:instance_exec, record, &sql)
else
sql
end
end
在此方法中,sql
是您的lambda条件,owner
计算Checkin
实例。
Lambdas回复to_proc
,以满足if
条件。所以真正发生的事情是:
Checkin.find(1).instance_exec(nil) {"evaluations.placement_id ... "}
这不起作用,因为nil
是一个参数,所以块应该有一个参数(你已经知道lambdas检查arity)。当您执行lambda {|a| ... }
时,a
为nil
,因为record
为nil
(因为其值未传递给interpolate
方法并且nil
是默认值。)