John Elent
John Elent

Reputation: 103

Update has_many association from array of ids

I have 2 models connected with has_many through association. Book can have many authors and Author many books.

In AuthorsController in update action from API I get author's id and an array of books ids Parameters: {"books"=>"3,4,5", "id"=>"1"} and I need to update author's books with them. What is the best way to achieve that?

I tried author.books << Book.where(id: params[:books]) but the problem here is that if a book is already in author's books it will be duplicated.

Upvotes: 5

Views: 17243

Answers (4)

Alessandro Moreira
Alessandro Moreira

Reputation: 461

The most efficient way is to use: has_many Association Reference. vide: Rails 4 Guides

4.3.1 Methods Added by has_many

When you declare a has_many association, the declaring class automatically gains 16 methods related to the association:

collection(force_reload = false)
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=(objects)
collection_singular_ids
**collection_singular_ids=(ids)**
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {}, ...)
collection.create(attributes = {})
collection.create!(attributes = {})

Example:

enter image description here

Upvotes: 15

Broman
Broman

Reputation: 81

I know this might be too late, but you could also try

author.book_ids.push(params[:books]).flatten.uniq!

it'd be a little shorter and a single request.

Upvotes: 2

Andy Gauge
Andy Gauge

Reputation: 1428

If param[:books] is a string of numbers, you can convert it to an array with

"3,4,5".split(',').map {|x| x.to_i}
-> [3, 4, 5]

Then with Rails Collection methods, author.book_ids should return an array that can be subtracted. So in all the feature would resemble:

author.books << Book.where(id: params[:books].split(',').map {|x| x.to_i} - author.book_ids)

Upvotes: -1

Oss
Oss

Reputation: 4322

author.books << Book.where(id: params[:books])

Performs unnecessary database queries.

You may need to remove the books that are already in the database

books_array = params[:books].split(',') - author.books.pluck(:id)
books_array.each do |a|
  author.author_books.build(book_id: a)
end

# I assumed author_books is the join table

Upvotes: 3

Related Questions