关于 inverse_of 的困惑与探究

来源:互联网 发布:highlight.js显示行号 编辑:程序博客网 时间:2024/05/17 01:06

最近在使用 关联 的时候,由于一点手误遇到了些问题,于是花了一下午时间来仔细读了 Guide 中关于 Active Record Associations 的部分。在看到 inverse_of 时,感觉自己突然一下就懵了。

问题缘由

我对 inverse_of 的困惑并不是在实际使用中产生的,即使不了解它也能在项目中愉快的玩耍,这似乎又旁证了 Rails 是一个很智能的框架。

不过它的 Guide 文档里这一部分就有些描述不清了(或说自相矛盾?)。关于 inverse_of 的功用倒是没有什么疑惑,只是被这混乱的文档弄得不知道哪些情况下需要去显式的声明 inverse_of , 哪些情况下 inverse_of 又是无效果的。

问题描述

Guide 中关于 inverse_of 的解释:http://guides.rubyonrails.org/association_basics.html#bi-directional-associations

其中有两处说明让我很费解:

其一:

There are a few limitations to inverse_of support:
1. They do not work with :through associations.
2. They do not work with :polymorphic associations.
3. They do not work with :as associations.
4. For belongs_to associations, has_many inverse associations are ignored.

按第四条所说的,has_many 的关联是无效的,但是 Guide 中的栗子便是使用的 has_many, 而且很好的证明了 inverse_of 的效果。

【2016.6.17】 这个其实没有疑问,这是指在 has_many / belongs_to 关联的两端都声明 inverse_of,只会按 belongs_to 一方的声明处理

其二:

Every association will attempt to automatically find the inverse association and set the :inverse_of option heuristically (based on the association name). Most associations with standard names will be supported. However, associations that contain the following options will not have their inverses set automatically:
1. :conditions 
2. :through
3. :polymorphic 
4. :foreign_key

按这个说法,只要是按约定命名的 关联 会自动加上 inverse_of, 那么,演示用例是按约定命名的吧,也不属于下面声明的四种情况,为什么加与不加是有差别的?

【2016.6.17】 这个其实也没有疑问,这是因为 Rails 版本变迁,我当时看得文档没来得及更新导致的。文章后面部分已经解释了这个问题,这里提前把结论贴一下。

动手验证

验证环境 Rails 版本:4.2.1

定义模型:

# a.rbclass A < ActiveRecord::Base    has_many :bend# b.rbclass B < ActiveRecord::Base    belongs_to :aend

测试结果:

2.2.1 :001 > a = A.create :name => 'ichou' => #<A id: 2, name: "ichou", created_at: "2015-04-04 06:41:41", updated_at: "2015-04-04 06:41:41">2.2.1 :002 > b1 = B.create :name => 'kindle', :a => a => #<B id: 3, name: "kindle", a_id: 2, created_at: "2015-04-04 06:42:45", updated_at: "2015-04-04 06:42:45">2.2.1 :003 > b2 = B.create :name => 'Air', :a => a => #<B id: 4, name: "Air", a_id: 2, created_at: "2015-04-04 06:43:10", updated_at: "2015-04-04 06:43:10">2.2.1 :004 > a.name.object_id => 702646376541202.2.1 :005 > b1.a.name.object_id => 702646376541202.2.1 :006 > b2.a.name.object_id => 70264637654120

object_id 全都一样,说明 inverse_of 已经被启用了。事实上,即使严格按照 Guide 的案例来做,你也会发现结果全是 True,而不是 Guide 所说的结果。

结论:在 4.1+ 的 Rails 中,即使不手动声明 inverse_of ,has_many 关联也会自动创建,而且是有效的!

总结与使用

  1. inverse_of 的作用在于关联模型间共用实例,而不是让不同的查询在内存中存在多份 Copies. 
    实际运用中可以带来两个好处,一是减少数据库查询;二是在对 关联对象 修改数据后写入数据前,保证从任何一方取得的值都是最新的。

  2. 从 4.1 开始,基本的关联类型(has_many, has_one, belongs_to),若按约定命名,不需要再手动设定 inverse_of

  3. (当时的)Guide 的相关用例是有问题的,或者说是适用于老版本的 Rails,而不是当前版本。
    因为给 basic associations\* 自动添加 inverse_of 是在 Rails 4.1 加入的特性。
    事实上,关于 has_many 的那个例子,在 4.1 及以后的版本中已经不能复现了。

  4. inverse_of 已经支持 has_many 是从 3.2.1 开始的,4.1 开始支持自动添加 inverse_of, 但是 has_many :through 仍然需要显式声明 inverse_of

  5. 使用没有持久化的关联对象时,根据需要使用 inverse_of,否则反向调用会得到 nil 或者还未更新的 对象。详见topics/6426


感谢 社区 和 stackoverflow 上的大大们,感谢微信群 成都Ruby群 里的星哥。

比较新手向的帖子,若有纰漏,烦请指正

0 0
原创粉丝点击