Rails Associations with Books and Genres (has_and_belongs_to_many)
While developing a Rails project on my own based on books and genres, I discovered a new way to delineate the relationships within the two associated models. Up until now, I had been employing the has_many :through association to construct the many-to-many relationships. When reading through the Rails documentation, I found that “has_and_belongs_to_many” performs a similar function and may be preferable in certain situations.
In this domain, Book has many Genres, and a Genre can have many Books (Books > — < Genres). In my classes, I can state the following:
class Book < ApplicationRecord
has_and_belongs_to_many :genres
endclass Genre < ApplicationRecord
has_and_belongs_to_many :books
end
It is important to note that I do not have to create a model for BookGenre, as I would in a has_many :through relationships. However, I still need to create a join table in the database. It took me awhile to find the right Rails command for creating that table:
rails g migration create_books_genres book_id:integer
genre_id:integer
Both “books” and “genres” are pluralized in this join table. The naming and punctuation is important for ActiveRecord to find the join table when later seeding data and writing queries.
class CreateBooksGenres < ActiveRecord::Migration[6.0]
def change
create_table :books_genres do |t|
t.integer :book_id
t.integer :genre_id
end
end
end
Once I migrated the table, then I was able to create ActiveRecord associations between instances of the Book and Genres model. In the seeds file below, I could shovel in an instance of a genre into an array of genres associated with a certain book.
# seeds.rbyellow_house = Book.create(name: "The Yellow House", price: 10.00)
memoir = Genre.create(name: "memoir")
yellow_house.genres << memoir
How do you choose between has_many :through and has_and_belongs_to_many?
If your relationship model is something that you need to interact with on its own, then you would create a has_many :through relationship. For example, in a domain with a patient, appointment, and doctor, the Appointment (relationship) model would be a class that you will probably need to access and work with in your application. Therefore, you would construct a has_many :through relationship. According to the Rails documentation, you would also use the has_many :through relationship “if you need validations, callbacks, or extra attributes on the join model.”
In contrast, with the BookGenre relationship, I would only be building upon the Book and Genre models. The BookGenre model is not a class I would call upon on its own. The has_and_belongs_to_many association thus eliminates the process of creating a model that lacks functionality.
Source: https://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association