BigBinary Blog

We write about Ruby on Rails, React.js, React Native, remote work, open source, engineering and design.

Rails 6.1 adds support for belongs_to to has_many inversing

This blog is part of our Rails 6.1 series.

Before Rails 6.1, we could only traverse the object chain in one direction - from has_many to belongs_to. Now we can traverse the chain bi-directionally.

The inverse_of option, both in belongs_to and has_many is used to specify the name of the inverse association.

Let's see an example.

1class Author < ApplicationRecord
2  has_many :books, inverse_of: :author
5class Book < ApplicationRecord
6  belongs_to :author, inverse_of: :books

Before Rails 6.1

has_many to belongs_to inversing

1irb(main):001:0> author =
2irb(main):002:0> book =
3irb(main):003:0> author ==
4=> true

In the above code, first we created the author and then a book instance through the has_many association.

In line 3, we traverse the object chain back to the author using the belongs_to association method on the book instance.

belongs_to to has_many inversing

1irb(main):001:0> book =
2irb(main):002:0> author = book.build_author
3irb(main):003:0> author.books
4=> #<ActiveRecord::Associations::CollectionProxy []>

In the above case, we created the book instance and then we created the author instance using the method added by belongs_to association.

But when we tried to traverse the object chain through the has_many association, we got an empty collection instead of one with the book instance.

After changes in Rails 6.1

The belongs_to inversing can now be traversed in the same way as the has_many inversing.

1irb(main):001:0> book =
2irb(main):002:0> author = book.build_author
3irb(main):003:0> author.books
4=> #<ActiveRecord::Associations::CollectionProxy [#<Book id: nil, author_id: nil, created_at: nil, updated_at: nil>]>

Here we get the collection with the book instance instead of an empty collection.

We can also verify using a test.

1class InverseTest < ActiveSupport::TestCase
3  def test_book_inverse_of_author
4    author =
5    book =
7    assert_equal, author
8  end
10  def test_author_inverse_of_book
11    book =
12    author = book.build_author
14    assert_includes author.books, book
15  end

In previous Rails versions, the test cases would fail.

1# Running:
7Expected #<ActiveRecord::Associations::CollectionProxy []> to include #<Book id: nil, author_id: nil, created_at: nil, updated_at: nil>.
9Finished in 0.292532s, 6.8369 runs/s, 10.2553 assertions/s.
102 runs, 3 assertions, 1 failures, 0 errors, 0 skips

In Rails 6.1, both the tests will pass.

1# Running:
5Finished in 0.317668s, 6.2959 runs/s, 9.4438 assertions/s.
62 runs, 3 assertions, 0 failures, 0 errors, 0 skips

Check out this pull request for more details.

Siddharth Shringi in Rails, Rails 6.1
January 19, 2021

Subscribe to our newsletter