Model Relationships (Associations)
A review is always about a book, and a book collects many reviews. You could look them up by hand with Review.where(book_id: book.id), but Rails lets you declare the relationship once and then say book.reviews. Getting the declaration right comes down to one question: which table holds the foreign key?
The rule
The table with the foreign key belongs_to; the other side has_many.
class Review < ApplicationRecord
belongs_to :book # reviews.book_id
belongs_to :member # reviews.member_id
end
class Book < ApplicationRecord
has_many :reviews, dependent: :destroy
end
class Member < ApplicationRecord
has_many :reviews
has_many :reviewed_books, through: :reviews, source: :book
endWhat each gives you:
belongs_to :book—review.book,review.book = some_book. Since Rails 5 the book is required: a review without one fails validation. Addoptional: trueto allow none.has_many :reviews—book.reviews,book.reviews.create(rating: 5, member: m),book.reviews.count.has_one— like has_many but for a single record, such ashas_one :profile.has_many :through— reach across a join model. Members and books are many-to-many, and the reviews table sits between them.dependent: :destroy— destroying the book destroys its reviews first, so none are left orphaned.
The foreign key column comes from the migration: book:references creates book_id. Rails guesses names from the association: belongs_to :book looks for book_id and the Book class.
The shortcut
Draw the tables and find the _id column. The model whose table has it gets belongs_to. The model it points at gets has_many (or has_one). If two models point at each other through a third table, add has_many :through on each.
Find the foreign keys
The
reviewstable hasbook_idandmember_id.
The trap is writing belongs_to on the side without the column, for example Book belongs_to :review. Rails would look for a review_id column on books that does not exist.
Drills
Answer fast — trust the rule.
The reviews table has a book_id column. Which declaration goes in Review?
A new review is saved with no book. What happens by default?
Which association lets member.reviewed_books work?
A book with 3 reviews is destroyed, and has_many :reviews, dependent: :destroy is set. What happens?
Remember
- The table with the foreign key belongs_to; the other side has_many.
- Shortcut: find the _id column; through for many-to-many.
- Trap: belongs_to on the side without the column.
