What Does Inverse_Of Do? What Sql Does It Generate?

I'm trying to get my head around inverse_of and I do not get it.

What does the generated sql look like, if any?

Does the inverse_of option exhibit the same behavior if used with :has_many, :belongs_to, and :has_many_and_belongs_to?

Sorry if this is such a basic question.

I saw this example:

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

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

7 Answers

From the documentation, it seems like the :inverse_of option is a method for avoiding SQL queries, not generating them. It's a hint to ActiveRecord to use already loaded data instead of fetching it again through a relationship.

Their example:

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

In this case, calling dungeon.traps.first.dungeon should return the original dungeon object instead of loading a new one as would be the case by default.

7

I think :inverse_of is most useful when you are working with associations that have not yet been persisted. E.g.:

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

class Task < ActiveRecord::Base
  belongs_to :project, :inverse_of=>:tasks
end

Now, in the console:

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, ...>

Without the :inverse_of arguments, t.project would return nil, because it triggers an sql query and the data isn't stored yet. With the :inverse_of arguments, the data is retrieved from memory.

3

After this pr () inverse_of is not required in most cases.

Active Record supports automatic identification for most associations with standard names. However, Active Record will not automatically identify bi-directional associations that contain a scope or any of the following options:

  • :through
  • :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

In the above example, a reference to the same object is stored in the variable a and in the attribute writer.

3

Just an update for everyone - we just used inverse_of with one of our apps with a has_many :through association


It basically makes the "origin" object available to the "child" object

So if you're using the Rails' example:

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

Using :inverse_of will allow you to access the data object that it's the inverse of, without performing any further SQL queries

When we have 2 models with has_many and belongs_to relationship, it's always better to use inverse_of which inform ActiveRecod that they belongs to the same side of the association. So if a query from one side is triggered, it will cache and serve from cache if it get triggered from the opposite direction. Which improves in performance. From Rails 4.1, inverse_of will be set automatically, if we uses foreign_key or changes in class name we need to set explicitly.

Best article for details and example.

0

If you have a has_many_through relation between two models, User and Role, and want to validate the connecting model Assignment against non existing or invalid entries with validates_presence of :user_id, :role_id, it is useful. You can still generate a User @user with his association @user.role(params[:role_id]) so that saving the user would not result in a failing validation of the Assignment model.

Please take a look 2 two useful resources

And remember some limitations of inverse_of :

does not work with :through associations.

does not work with :polymorphic associations.

for belongs_to associations has_many inverse associations are ignored.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Alexander Ross

Alexander Ross

Gaming, Esports & Interactive Media Writer

Alexander Ross has covered the video game industry for a decade, writing deep dives on game design, esports tournaments, VR developments, and gaming culture.

Share this article
Twitter Facebook Pinterest