inverse_of做什么? 它产生了什么SQL?
我试图把我的头inverse_of
,我不明白。
什么生成的SQL看起来像,如果有的话?
如果与:has_many
, :belongs_to
和:has_many_and_belongs_to
一起使用, inverse_of
选项是否performance出相同的行为?
对不起,如果这是一个基本的问题。
我看到这个例子:
class Player < ActiveRecord::Base has_many :cards, :inverse_of => :player end class Card < ActiveRecord::Base belongs_to :player, :inverse_of => :cards end
从文档中 ,似乎是:inverse_of
选项是一种避免SQL查询的方法,不会生成它们。 这是ActiveRecord使用已经加载的数据而不是通过关系重新获取的暗示。
他们的例子:
class Dungeon < ActiveRecord::Base has_many :traps, :inverse_of => :dungeon has_one :evil_wizard, :inverse_of => :dungeon end class Trap < ActiveRecord::Base belongs_to :dungeon, :inverse_of => :traps end class EvilWizard < ActiveRecord::Base belongs_to :dungeon, :inverse_of => :evil_wizard end
在这种情况下,调用dungeon.traps.first.dungeon
应该返回原来的dungeon
对象,而不是加载一个新的默认情况。
我认为:inverse_of
当你使用尚未被保存的关联时, :inverse_of
是最有用的。 例如:
class Project < ActiveRecord::Base has_many :tasks, :inverse_of=>:project end class Task < ActiveRecord::Base belongs_to :project, :inverse_of=>:tasks end
现在,在控制台中:
irb> p = Project.new => #<Project id: nil, name: nil, ...> irb> t = p.tasks.build => #<Task id: nil, project_id: nil, ...> irb> t.project => #<Project id: nil, name: nil, ...>
如果没有:inverse_of参数,t.project将会返回nil,因为它会触发一个sql查询并且数据还没有被存储。 使用:inverse_of参数,数据从内存中检索。
从Rails 5.0的文档和伟大的。
指南
双向关联
协会在两个方向上工作是正常的,需要在两个不同的模型上声明:
class Author < ApplicationRecord has_many :books end class Book < ApplicationRecord belongs_to :author end
默认情况下,Active Record不知道这些关联之间的连接。 这可能导致对象的两个副本不同步:
a = Author.first b = a.books.first a.first_name == b.author.first_name # => true a.first_name = 'Manny' a.first_name == b.author.first_name # => false
发生这种情况是因为a和b.author是同一个数据的两个不同的内存中表示,并且没有一个会自动从另一个的更改中刷新。 Active Record提供:inverse_of选项,以便您可以将这些关系通知给它:
class Author < ApplicationRecord has_many :books, inverse_of: :author end class Book < ApplicationRecord belongs_to :author, inverse_of: :books end
通过这些更改,Active Record将只加载作者对象的一个副本,从而防止出现不一致并提高应用程序的效率:
a = Author.first b = a.books.first a.first_name == b.author.first_name # => true a.first_name = 'Manny' a.first_name == b.author.first_name # => true
inverse_of支持有一些限制:
他们不通过协会工作。 它们不适用于:多态关联。 他们不作为协会工作。
对于belongs_to关联,has_many反向关联被忽略。 每个关联都将尝试自动查找逆关联并启发式地设置inverse_of选项(根据关联名称)。 大多数与标准名称的关联将被支持。 但是,包含以下选项的关联不会自动设置其逆向:
- :条件
- :通过
- 多态
- :foreign_key
只是每个人的更新 – 我们只是has_many :through
关联在我们的应用程序中使用了inverse_of
它基本上使“起源”对象可用于“子”对象
所以如果你使用Rails的例子:
class Dungeon < ActiveRecord::Base has_many :traps, :inverse_of => :dungeon has_one :evil_wizard, :inverse_of => :dungeon end class Trap < ActiveRecord::Base belongs_to :dungeon, :inverse_of => :traps validates :id, :presence => { :message => "Dungeon ID Required", :unless => :draft? } private def draft? self.dungeon.draft end end class EvilWizard < ActiveRecord::Base belongs_to :dungeon, :inverse_of => :evil_wizard end
使用:inverse_of
将允许您访问与之相反的数据对象,而不执行任何其他SQL查询
当我们有has_many和belongs_to关系的2个模型时,最好使用inverse_of来通知ActiveRecod它们属于关联的同一侧。 所以如果一个查询从一个方面被触发,它将caching,并从caching服务,如果从相反的方向触发。 这在性能上有所提高。 从Rails 4.1开始,inverse_of会自动设置,如果我们使用foreign_key或者更改类名,我们需要明确设置。
最好的文章的细节和例子。
http://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations
如果在两个模型User和Role之间有has_many_through
关系,并且要validation连接模型Assignment与validates_presence of :user_id, :role_id
非现有条目或无效条目,那么这很有用。 您仍然可以使用其关联@user.role(params[:role_id])
生成一个User @user.role(params[:role_id])
用户,以便保存用户不会导致对Assignment模型的validation失败。
请看看两个有用的资源
- https://www.viget.com/articles/exploring-the-inverse-of-option-on-rails-model-associations
- http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Bi-directional+associations
记住inverse_of
一些限制:
不适用于:通过关联。
不适用于:多态关联。
对于belongs_to关联has_many反向关联被忽略。