DEV Community

Cover image for Work with :through associations made easy
Augusts Bautra
Augusts Bautra

Posted on • Edited on

3

Work with :through associations made easy

As explained in Rails association reference, defining a has_many association gives 17 methods.

We'll focus on the collection_singular_ids=(ids) part.

class Book
  has_many :book_genres
  has_many :genres, through: :book_genres
end

class Genre
  has_many :book_genres
  has_many :books, through: :book_genres
end

# the tie model
class BookGenre
  belongs_to :book
  belongs_to :genre  
end
Enter fullscreen mode Exit fullscreen mode

Given a book, we'd get a genre_ids= (from has_many :genres). This method is very powerful. It's designed in such a way that you can use the value as-is straight from controller params in mass-assigment, and only the genres you specified would remain. Yes, genre_ids= will take care of identifying assocs being removed and new ones being added and only apply the change, not touching unchanged records!

book.genre_ids #=> [1, 2]

# this will delete the BookGenre record tying this book to genre 1
# and create a new one tying it to genre 3, leaving tie to 2 unchanged!
book.update!(genre_ids: ["2", "3"])

book.genre_ids #=> [2, 3] 
Enter fullscreen mode Exit fullscreen mode

This only leaves validation errors. Say you want to validate that every book has at least one genre specified.

Easy, you can even use a macro validation:

Book.validates :genre_ids, length: { minimum: 1, message: :at_least_one_required }
Enter fullscreen mode Exit fullscreen mode

The last piece of the puzzle is how will the form react. You may need to align what field/helper you are using, so validation errors color the field nice and red on any problems.

<%= f.input :genre_ids %>

# or maybe

<%= f.association :genres %>
Enter fullscreen mode Exit fullscreen mode

Just make sure the param being submitted is :genre_ids, so mass-assignment works.

For StrongParams you may need to do this:

params.require(:book).permit(
  :title, genre_ids: []
).
Enter fullscreen mode Exit fullscreen mode

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more