What does inverse_of do? What SQL does it generate?

Ruby on-RailsActiverecord

Ruby on-Rails Problem Overview


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

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

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.

Solution 2 - Ruby on-Rails

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.

Solution 3 - Ruby on-Rails

After this pr (https://github.com/rails/rails/pull/9522) 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.

Solution 4 - Ruby on-Rails

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.

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

Solution 5 - Ruby on-Rails

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

Solution 6 - Ruby on-Rails

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.

Solution 7 - Ruby on-Rails

Solution 8 - Ruby on-Rails

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFedericoView Question on Stackoverflow
Solution 1 - Ruby on-RailstadmanView Answer on Stackoverflow
Solution 2 - Ruby on-RailsKenBView Answer on Stackoverflow
Solution 3 - Ruby on-RailsartamonovdevView Answer on Stackoverflow
Solution 4 - Ruby on-RailsGurudath BNView Answer on Stackoverflow
Solution 5 - Ruby on-RailsRichard PeckView Answer on Stackoverflow
Solution 6 - Ruby on-RailsInformatomView Answer on Stackoverflow
Solution 7 - Ruby on-RailsSamuel G. P.View Answer on Stackoverflow
Solution 8 - Ruby on-RailsLeo LeView Answer on Stackoverflow