Syncing rails model using nested_attributes

41 Views Asked by At

Is it possible to update a rails model to sync with provided nested attributes for that model?

Let's say I have a many to many association between a Lbrirary and Book model:

class Library < ApplicationRecord
    #id integer
    #name string
    #location string

    has_many :library_books, dependent: :destroy
    has_many :books, through: :library_books

    accepts_nested_attributes_for :library_books, allow_destroy: true
end

class Book < ApplicationRecord
    #id integer
    #name string
    #author name

    has_many :libraries, through: :library_books
    has_many :library_books
end

class LibraryBook < ApplicationRecord
  # id integer
  # book_id integer
  # library_id integer

  belongs_to :library
  belongs_to :book
end

class LibrariesController < ApplicationController
  def update
    @library = Library.find(params[:id])
    if @library.update(library_params)
      # Successful update
    else
      # Handle errors
    end
  end

  private

  def library_params
    params.require(:library).permit(:name, library_books_attributes: [:id, :book_id])
  end
end

Now, I send a request to the LibrariesController to create a new Library record along with several books:

{
  "id": 1,
  "library_books_attributes": [
      {
          id: 2
          book_id: 3
      }, 
      {
          book_id: 4
      },
      {
          book_id: 5
      }

  ],
  ...
}

How can I ensure that LibraryBook is updated such that Library with id 1 ONLY matches what's in the request. So, if a record already existed in LibraryBook where book_id was 12 that recored would be destroyed because it is not in library_books_attributes

So in short whatever is in the request for the child model will always be the complete list whether records have to be created/update/or deleted. Is this possible with build in active record/rails functionality or is it required to write a method to do this delta?

1

There are 1 best solutions below

0
smathy On BEST ANSWER

For the built-in functionality you'd need to have that element of the passed in list like { id: 12, _destroy: 1 }. For what you seem to want you'd need to just write your own library_books_attributes= method.