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
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]
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 }
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 %>
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: [] ).
Top comments (0)