I'm using Rails 6.1.3.2
and trying to make saving of nested attributes more efficient. The database used is MySQL.
BoardSet.rb
class BoardSet < ApplicationRecord
has_many :boards, inverse_of: :board_set, foreign_key: 'board_set_id', dependent: :destroy
accepts_nested_attributes_for :boards
Board.rb
class Board < ApplicationRecord
has_many :cells, inverse_of: :board, foreign_key: 'board_id', dependent: :destroy
accepts_nested_attributes_for :cells
Cell.rb
class Cell < ApplicationRecord
belongs_to :board, inverse_of: :cells, foreign_key: 'board_id'
There is Grape::API endpoint like this:
params do
requires :name, type: String, desc: 'BoardSet name'
optional :boards, as: :boards_attributes, type: Array[JSON] do
# more params
optional :cells, as: :cells_attributes, type: Array[JSON] do
# more params
end
end
end
post do
board_set = BoardSet.new(declared(params, include_missing: false))
board_set.save!
end
Summary of code basis: a BoardSet contains Boards which contain Cells. There is a Grape::API endpoint, where data can be passed to via PUT
, which is then saved within the database.
Saving with board_set.save!
is very inefficient. A BoardSet typically contains 40
Boards, where each contain maybe 20
Cells. So in total there are more than 40 * 20 = 800
SQL INSERT
statements (a single one for each Cell).
I've tried these attempts for improvements:
BoardSet.import [board_set], recursive: true
, but recursive
is only supported for PostgeSQL, so not for MySQL, which I'm using.board_set
including boards with empty cells, then transferring the ID of the boards to the cells and save them in bulk. However I didn't manage to remove the cells, I've tried to use cloned_board = board.dup
and cloned_board.cells = []
for saving empty copies, but it still saves all cells at cloned_board_set.save!
(where cloned_board_set
contains cloned boards with emtpy cells).accepts_nested_attributes_for :cells
from Board.rb
in order to prevent board_set.save!
from automatically saving all the cells. However this results in an exception in BoardSet.new(declared(params, include_missing: false))
since the parameter cells_attributes
it not known.Instead of saving all the objects with one line try these 3 steps:
After rails 6 we can do insert all
board_set = BoardSet.new(declared(params[:name], include_missing: false))