ruby-on-rails - Rails 关联方法如何工作?

标签 ruby-on-rails activerecord model-associations

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 对象数组?因为如果我们实现自己的名为 articlesUser 实例方法,并编写我们自己的实现,它会为我们提供与关联方法完全相同的结果,即在 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/

相关文章:

backbone.js - 从 Mustache 模板访问嵌套主干模型属性

ruby-on-rails - 将自由格式的 sql 转换为事件记录

ruby-on-rails - Rails 3 与 Sinatra

ruby-on-rails - ActiveRecord Migration 后的括号 [5.1] 是什么,它是如何工作的?

Ruby:ActiveRecord - 第 N 行的索引

mysql - Rails 查找方法没有使用我的外键

jquery - bootstrap datepicker js禁用 future 日期

ruby-on-rails - Ruby on rails : Bundler not installed when deploying using capistrano

ruby-on-rails - 是否支持在 Heroku 上使用 C 扩展 Ruby?

Android:为 MySQL 和 SQLite 数据库设计