Rails 关联方法如何工作?让我们考虑这个例子
class User < ActiveRecord::Base
has_many :articles
end
class Article < ActiveRecord::Base
belongs_to :user
end
现在我可以做类似的事情
@user = User.find(:first)
@user.articles
这会获取属于该用户的文章。到目前为止一切顺利。
现在我可以在满足某些条件的情况下继续查找这些文章。
@user.articles.find(:all, :conditions => {:sector_id => 3})
或者简单地声明和关联方法,例如
class User < ActiveRecord::Base
has_many :articles do
def of_sector(sector_id)
find(:all, :conditions => {:sector_id => sector_id})
end
end
end
然后做
@user.articles.of_sector(3)
现在我的问题是,这个 find
如何处理使用关联方法获取的 ActiveRecord
对象数组?因为如果我们实现自己的名为 articles
的 User
实例方法,并编写我们自己的实现,它会为我们提供与关联方法完全相同的结果,即在 fetch 数组上查找ActiveRecord
对象将无法工作。
我的猜测是,关联方法将某些属性附加到获取的对象数组,以便使用 find
和其他 ActiveRecord
方法进行进一步查询。在这种情况下,代码执行的顺序是什么?我如何验证这一点?
最佳答案
它的实际工作原理是关联对象是一个“代理对象”。具体类是AssociationProxy 。如果您查看该文件的第 52 行,您将看到:
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
通过这样做,该对象上不再存在诸如 class
之类的方法。因此,如果您在此对象上调用class
,则会缺少方法。因此,为代理对象实现了一个 method_missing
,它将方法调用转发到“目标”:
def method_missing(method, *args)
if load_target
unless @target.respond_to?(method)
message = "undefined method `#{method.to_s}' for \"#{@target}\":#{@target.class.to_s}"
raise NoMethodError, message
end
if block_given?
@target.send(method, *args) { |*block_args| yield(*block_args) }
else
@target.send(method, *args)
end
end
end
目标是一个数组,所以当你在这个对象上调用class
时,它会说它是一个数组,但这只是因为目标是一个数组,实际的类是一个AssociationProxy,但是你再也看不到了。
因此,您添加的所有方法(例如 of_sector
)都会添加到关联代理中,以便直接调用它们。 []
和 class
等方法未在关联代理上定义,因此它们被发送到目标(一个数组)。
为了帮助您了解这是如何发生的,请将以下内容添加到您的 Association_proxy.rb 本地副本中该文件的第 217 行:
Rails.logger.info "AssociationProxy forwarding call to `#{method.to_s}' method to \"#{@target}\":#{@target.class.to_s}"
如果您不知道该文件在哪里,命令 gem which 'active_record/associations/association_proxy'
会告诉您。现在,当您在 AssociationProxy 上调用 class
时,您将看到一条日志消息,告诉您它正在将其发送到目标,这应该可以让您更清楚地了解发生了什么。这全部适用于 Rails 2.3.2,在其他版本中可能会发生变化。
关于ruby-on-rails - Rails 关联方法如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1529606/