Я использую 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
(по одному для каждой ячейки).
Я попробовал эти попытки улучшения:
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
неизвестен.Вам нужно представить доску в виде 20 записей в таблице ячеек? Может ли это быть простой столбец массива в таблице досок?
@engineersmnky - спасибо, я также уже нашел эту страницу, предлагающую перезаписать autosave_associated_record_for_XXX
, но, поскольку она не задокументирована, я подумал, что должен быть лучший способ. @ user229044 да, мне тоже это интересно. Я не эксперт в архитектуре реляционных баз данных. Я читал, что многие строки в таблице подходят, но я также подумал, что сохранение ячеек непосредственно на досках, возможно, было бы лучшим решением?!
Вместо того, чтобы сохранять все объекты в одной строке, попробуйте выполнить следующие 3 шага:
После рельсов 6 мы можем сделать вставить все
board_set = BoardSet.new(объявлен(params[:name], include_missing: false))
Спасибо, это именно то, что я пробовал. Однако мне не удалось сохранить платы без вложенных ячеек. Я пытался продублировать их с помощью board.dup
, а затем удалить ячейки с помощью duplicated_board.cells = []
, но сохранение с помощью duplicated_board.save
все равно сохраняет все ячейки. Я понятия не имею, почему.
попробуйте после удаления accepts_nested_attributes_for
ок, наконец-то я узнал, почему ячейки создавались всегда, даже если я удалил их из параметров. В классе Board
был некий крючок after_create :populate_cells
, где ячейки автоматически создавались в соответствии с размерами доски, если они не существовали. Что имеет смысл при создании пустых досок, но не имеет смысла, если я хочу сначала создать доски, а затем все ячейки за одну массовую операцию. Наконец я создал класс BoardNoAutocells < Board
без after_create
хука и последовал вашему ответу. Кажется, это не самое чистое решение, но, по крайней мере, решение.
Возможно, вам придется реализовать себя, разделив параметры внутри, но функциональность, которую, я полагаю, вы ищете, обеспечивается ActiveRecord::Persistence::ClassMethods#upsert_all . На этой странице предлагается перезаписать метод
autosave_associated_record_for_XXX
для реализации этой функциональности, хотя я его не проверял, поэтому не могу подтвердить.