inverse_of做什么?它产生什么SQL?

Fed*_*ico 133 activerecord ruby-on-rails

我正试图让我的头脑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
Run Code Online (Sandbox Code Playgroud)

tad*_*man 117

文档中看,该: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
Run Code Online (Sandbox Code Playgroud)

在这种情况下,调用dungeon.traps.first.dungeon应返回原始dungeon对象,而不是默认情况下加载新对象.

  • 这对我来说都很奇怪,因为在我看来,默认情况下你总是想要这种行为,并且只有在无法推断出关联名称时才需要使用:inverse_of.此外,定义中的不一致也很麻烦,但在一些情况下它帮助了我.我不应该只是把它粘在各处? (48认同)
  • @Ibrahim看看这个,它在23天前合并了!https://github.com/rails/rails/pull/9522 (17认同)
  • 有意义的是忽略belongs_to关联的逆,因为记录A的父记录的子节点不能保证是记录A - 它可能是记录A的兄弟.记录A的子节点的父节点,但是,保证是记录A. (6认同)
  • 您是否理解文档中的注释:"对于belongs_to关联has_many反向关联被忽略." 但是doc使用了那个确切的例子.我在这里错过了什么? (5认同)
  • 未来的读者可能会从这个[博客](http://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations)获得帮助......:D (2认同)
  • 从版本4.1开始,Rails将尝试为您自动设置`:inverse_of`选项([pull request](https://github.com/rails/rails/pull/10886)).[...]显然,当名字不匹配时,这就会崩溃.[...]在这种情况下,:inverse_of必须明确设置为正确的名称,因此仍然值得知道:inverse_of是如何工作的.资料来源:https://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations (2认同)

Ken*_*enB 38

:inverse_of当您使用尚未持久化的关联时,我认为最有用.例如:

class Project < ActiveRecord::Base
  has_many :tasks, :inverse_of=>:project
end

class Task < ActiveRecord::Base
  belongs_to :project, :inverse_of=>:tasks
end
Run Code Online (Sandbox Code Playgroud)

现在,在控制台中:

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, ...>
Run Code Online (Sandbox Code Playgroud)

如果没有:inverse_of参数,t.project则会返回nil,因为它会触发sql查询并且数据尚未存储.使用:inverse_of参数,可以从内存中检索数据.

  • 我对accepts_nested_attributes_for 有问题。默认情况下,仅显示现有关联对象的嵌套属性(编辑操作)。例如,如果您想创建一个具有 3 个关联对象的对象,则您的模型中应该有 Model.new(新操作)和 :inverse_of。 (2认同)

art*_*dev 13

从Rails 5.0的文档和伟大的.

指南

双向关联

协会在两个方向工作是正常的,需要在两个不同的模型上进行声明:

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
Run Code Online (Sandbox Code Playgroud)

默认情况下,Active Record不知道这些关联之间的连接.这可能导致对象的两个副本不同步:

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
Run Code Online (Sandbox Code Playgroud)

发生这种情况是因为a和b.author是相同数据的两个不同的内存中表示,并且两个都不会自动从另一个的更改中刷新.Active Record提供:inverse_of选项,以便您可以告知它们这些关系:

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
Run Code Online (Sandbox Code Playgroud)

通过这些更改,Active Record将仅加载作者对象的一个​​副本,从而防止出现不一致并使您的应用程序更有效:

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
Run Code Online (Sandbox Code Playgroud)

inverse_of支持有一些限制:

它们不适用于:通过协会.它们不兼容:多态关联.它们不适用于:作为协会.

对于belongs_to关联,忽略has_many反向关联.每个关联都将尝试自动查找反向关联,并启发式地设置:inverse_of选项(基于关联名称).将支持大多数与标准名称的关联.但是,包含以下选项的关联不会自动设置其反转:

  • :条件
  • :通过
  • 多态
  • :foreign_key

  • @Arslan Ali 我认为这是关于在 not 上触发 sql 查询。这与它们是否相等无关。 (3认同)
  • 我使用的是 Rails 5,无论您是否添加“inverse_of”,“a.first_name == b.author.first_name”的结果始终为 true。 (2认同)

Ric*_*eck 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
Run Code Online (Sandbox Code Playgroud)

使用:inverse_of将允许您访问与其相反的数据对象,而不执行任何进一步的SQL查询


Gur*_* BN 5

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

有关详细信息和示例的最佳文章。

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