Ruby on Rails: как эффективно сохранять вложенные атрибуты (рекурсивно)?

Я использую Rails 6.1.3.2 и пытаюсь сделать сохранение вложенных атрибутов более эффективным. Используемая база данных — 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.рб

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'

Существует конечная точка Grape::API, такая:

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

Функциональность

Краткое описание основы кода: BoardSet содержит Boards, которые содержат ячейки. Существует конечная точка Grape::API, куда можно передавать данные через PUT, которые затем сохраняются в базе данных.

Проблема

Экономить с помощью board_set.save! очень неэффективно. BoardSet обычно содержит 40 Доски, каждая из которых может содержать 20 Ячейки. Таким образом, всего имеется более 40 * 20 = 800 операторов SQL INSERT (по одному для каждой ячейки).

Попытки решения

Я попробовал эти попытки улучшения:

  • используйте драгоценный камень activerecord-import , который, похоже, способен выполнять массовые вставки для таких ситуаций. Однако я думаю, что мне понадобится что-то вроде BoardSet.import [board_set], recursive: true, но recursive поддерживается только для PostgeSQL, а не для MySQL, который я использую.
  • сначала сохраните board_set включая доски с пустыми ячейками, затем перенесите ID досок в ячейки и сохраните их массово. Однако мне не удалось удалить ячейки, я пытался использовать cloned_board = board.dup и cloned_board.cells = [] для сохранения пустых копий, но он все равно сохраняет все ячейки в cloned_board_set.save! (где cloned_board_set содержит клонированные доски с пустыми ячейками).
  • Я попытался удалить accepts_nested_attributes_for :cells из Board.rb, чтобы предотвратить автоматическое сохранение всех ячеек board_set.save!. Однако это приводит к исключению BoardSet.new(declared(params, include_missing: false)), поскольку параметр cells_attributes неизвестен.

🤔 А знаете ли вы, что...
Ruby on Rails создан на основе языка программирования Ruby и был впервые выпущен в 2005 году.


52
1

Ответ:

Решено

Вместо того, чтобы сохранять все объекты в одной строке, попробуйте выполнить следующие 3 шага:

  1. Сохраните набор досок только с его именем, то есть без вложенных атрибутов.
  2. Сохраните все доски без вложенных атрибутов.
  3. Сохраните все ячейки

После рельсов 6 мы можем сделать вставить все

board_set = BoardSet.new(объявлен(params[:name], include_missing: false))