5

Rails 7 now allows automatic inverse_of detection for associations with scopes

 2 years ago
source link: https://blog.saeloun.com/2022/02/01/rails-7-inverse-of-automatic-inference
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Rails 7 now allows automatic inverse_of detection for associations with scopes

Feb 1, 2022 , by Swaathi Kakarla

2 minute read

ActiveRecord has many tricks up its sleeve. One of the many is this seemingly simple association option called inverse_of, which helps us to explicitly declare bi-directional associations.

Let’s have a look!

class Author < ApplicationRecord
  has_many :books, inverse_of: :author
end

class Book < ApplicationRecord
  belongs_to :author
end

Seems almost redundant, but it performs a very important function. When inverse_of is declared, one can update related models without explicitly writing update statements.

> author = Author.first
> book = author.books.first
> author.first_name == book.writer.first_name
=> true

> author.first_name = 'David'
> author.first_name == book.writer.first_name
=> true

As you can see, without actually updating the name of the author, inverse_of was able to relate the models.

Another useful element is that inverse_of stores a copy of the relation in the ActiveRecord query. So when you call upon the association, it does not need to load it from the database.

class Author < ApplicationRecord
  has_many :books
end

class Book < ApplicationRecord
  belongs_to :author, inverse_of: :books
end

As you can see, the inverse_of filter is now applied to the belongs_to relation.

> author = Author.first
> book = author.books.first
> book.author == author
=> true

However if the option was not applied:

> author = Author.first
> book = author.books.first
> book.author == author
# Author Load (0.1ms) SELECT "authors".* FROM "author" WHERE "author"."id" = 1 LIMIT 1
=> true

Before

Fortunately, Rails adds inverse_of to all belongs_to associations. Unfortunately, it does not do so when a scope is added to the association.

Let’s look at an example:

class Author < ApplicationRecord
  has_many :books, -> { visible }
end

class Book < ApplicationRecord
  belongs_to :author

  scope :visible, -> { where(visible: true) }
  scope :hidden, -> { where(visible: false) }
end

Now let’s perform the queries:

> author = Author.first
> book = author.books.first
> book.author == author
# Author Load (0.4ms)  SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> true

After

Thanks to changes in Rails 7, automatic inverse_of detection for associations with scopes now works!.

Let’s look at the same example again:

> author = Author.first
> book = author.books.first
> book.author == author
=> true

As you can see, no database query was made. This option is not enabled by default in Rails 7. To enable it please set this configuration option,

config.active_record.automatic_scope_inversing = true

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK