Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

As of Rails 5, when is :inverse_of necessary to set manually?

I've read in various places that it gets automatically inferred in some circumstances, but I've found the documentation on this feature to be hard to follow. Can anyone shed some light on the principles? Specifically when will Rails be able to infer and when will Rails not be able infer inverses?

I'd like to consider

  • polymorphic associations
  • single tabled inheritance (e.g. where BundleProduct and IndividualProduct both inherit from Product and use a products table)
  • regular has_one/has_many/belongs_to associations
  • has many through: associations

Some context: I'm maintaining a reasonably large 9-year-old Rails application with many, many tables. I'd like to have some guidance on which models require addition of inverse_of: rather than having to change every model in the system.

like image 316
Jack Kinsella Avatar asked Jan 09 '19 09:01

Jack Kinsella


2 Answers

If you do not set the :inverse_of record, the association will do its best to match itself up with the correct inverse. Automatic inverse detection only works on has_many, has_one, and belongs_to associations.
-- Rails API docs - ActiveRecord::Associations::ClassMethods

In cases where you have "non-standard" naming you may however need to provide the option:

The automatic guessing of the inverse association uses a heuristic based on the name of the class, so it may not work for all associations, especially the ones with non-standard names. -- Rails API docs - ActiveRecord::Associations::ClassMethods

For example:

class Pet < ApplicationRecord
  belongs_to :owner, class_name: 'User'
end

class User < ApplicationRecord
  has_many :pets
end

If we call pet.owner it may incur a database hit even if we have already loaded that record.

If we add the inverse_of option:

class Pet < ApplicationRecord
  belongs_to :owner, class_name: 'User', inverse_of: :pets
end

class User < ApplicationRecord
  has_many :pets, inverse_of: :owner
end

Now if we already have that owner record in memory then pet.owner will point to the same owner.

In general there is no harm in explicitly setting the inverse_of so you can set it for each case where you are unsure. You can also manually test if it is needed by seeing if accessing the assocation creates a DB query via the console or by using shoulda-matchers with your test suite.

like image 155
max Avatar answered Sep 28 '22 19:09

max


You can look up exact inferring procedure in ActiveRecord::Reflection::AssociationReflection#automatic_inverse_of.

Rails tries to detect inverse for has_many, has_one and belongs_to that do not have through, foreign_key options and scope and have standard names (this is also described in official guide).

like image 41
Vasfed Avatar answered Sep 28 '22 17:09

Vasfed