inverse_of有什么作用?它生成什么SQL?


143

我想尽我所能inverse_of,但我不明白。

生成的sql是什么样的(如果有)?

inverse_of如果与,和一起使用:has_many,该选项是否表现出相同的行为?:belongs_to:has_many_and_belongs_to

抱歉,这是一个基本问题。

我看到了这个例子:

class Player < ActiveRecord::Base
  has_many :cards, :inverse_of => :player
end

class Card < ActiveRecord::Base
  belongs_to :player, :inverse_of => :cards
end

Answers:


125

文档中看来,该: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对象,而不是像默认情况下那样加载新对象。


5
您是否理解文档中的注释:“对于belongs_to关联,has_many反向关联将被忽略。”。但是该文档使用了确切的示例。我在这里想念什么?
dynex

50
这一切对我来说都是很奇怪的,因为在我看来,默认情况下您总是会想要这种行为,并且仅在无法推断关联名称时才需要使用:inverse_of。定义中的不一致也很麻烦,但这在某些情况下帮助了我。有什么理由我不应该随处都贴它吗?
Ibrahim

18
@Ibrahim检查一下,它已经在23天前合并了!github.com/rails/rails/pull/9522
Hut8

6
可以忽略belongs_to关联的逆关系,因为不能保证记录A的父记录的子记录是记录A -它可以是记录A的同级。 ,是保证记录A.
大卫·阿尔德里奇

2
未来的读者可能会从此博客中获得帮助...:D
Arup Rakshit

42

: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参数,可以从内存中检索数据。


1
我对accepts_nested_attributes_for有问题。默认情况下,仅显示现有关联对象的嵌套属性(编辑操作)。例如,如果您想创建一个带有3个相关对象的对象,则您的模型中应该包含Model.new(新动作)和:inverse_of。
维克多·马可尼

同意在Rails 4和更高版本中的行为,但是在v3中可以正常工作(以后的版本除外,尽管旧语法在v3.2.13中仍然可以工作)。并请注意,在连接模型中,无法再验证ID的存在-仅是模型对象。在v4“逻辑”中,您似乎可以拥有一个没有ID的关联。
JosephK

确切地.. :inverse_of为我解决了一个以相同形式创建新的父实体和子实体的问题。
2013年

16

此公关(https://github.com/rails/rails/pull/9522)之后,在大多数情况下不需要inverse_of

Active Record支持自动识别大多数具有标准名称的关联。但是,Active Record不会自动识别包含范围或以下任何选项的双向关联:

  • :通过
  • :foreign_key
class Author < ApplicationRecord
  has_many :books, inverse_of: 'writer'
end

class Book < ApplicationRecord
  belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

a = Author.first
b = a.books.first
a.first_name == b.writer.first_name # => true
a.first_name = 'David'
a.first_name == b.writer.first_name # => true

在上面的示例中,对同一对象的引用存储在变量a和属性中writer


我正在使用Rails 5,无论您添加inverse_of与否,其结果a.first_name == b.author.first_name始终是正确的。
Arslan Ali

@ArslanAli感谢您的精彩评论,我更新了答案。
artamonovdev

5

只是对所有人的更新-我们只是将inverse_of其中一个应用与has_many :through关联


基本上,它使“原始”对象可用于“子”对象

因此,如果您使用的是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查询


5

当我们有2个具有has_many和belongs_to关系的模型时,最好使用inverse_of通知ActiveRecod它们属于关联的同一侧。因此,如果从一侧触发了查询,则如果从相反方向触发,它将缓存并从缓存中提供服务。从而提高了性能。从Rails 4.1开始,将自动设置inverse_of,如果我们使用foreign_key或更改类名,则需要显式设置。

有关细节和示例的最佳文章。

http://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations



3

如果您has_many_through在两个模型(用户和角色)之间有关系,并且想要使用来针对不存在或无效的条目验证连接模型的分配validates_presence of :user_id, :role_id,则将很有用。您仍然可以生成具有其关联的User @user,@user.role(params[:role_id])以便保存用户不会导致对Assignment模型的验证失败。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.