Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support on_duplicate_key_update for recursive imports #778

Merged
merged 4 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 14 additions & 1 deletion lib/activerecord-import/import.rb
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,15 @@ def import_without_validations_or_callbacks( column_names, array_of_attributes,

private

def associated_options(options, associated_class)
return options unless options.key?(:recursive_on_duplicate_key_update)

table_name = associated_class.arel_table.name.to_sym
options.merge(
on_duplicate_key_update: options[:recursive_on_duplicate_key_update][table_name]
)
end

def set_attributes_and_mark_clean(models, import_result, timestamps, options)
return if models.nil?
models -= import_result.failed_instances
Expand Down Expand Up @@ -963,7 +972,11 @@ def import_associations(models, options)

associated_objects_by_class.each_value do |associations|
associations.each_value do |associated_records|
associated_records.first.class.bulk_import(associated_records, options) unless associated_records.empty?
next if associated_records.empty?

associated_class = associated_records.first.class
associated_class.bulk_import(associated_records,
associated_options(options, associated_class))
end
end
end
Expand Down
48 changes: 48 additions & 0 deletions test/support/shared_examples/recursive_import.rb
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,54 @@ def should_support_recursive_import
end
end
end

describe "recursive_on_duplicate_key_update" do
let(:new_topics) { Build(1, :topic_with_book) }

setup do
Topic.import new_topics, recursive: true
end

it "updates associated objects" do
new_author_name = 'Richard Bachman'
topic = new_topics.first
topic.books.each do |book|
book.author_name = new_author_name
end

assert_nothing_raised do
Topic.import new_topics,
recursive: true,
on_duplicate_key_update: [:id],
recursive_on_duplicate_key_update: {
books: { conflict_target: [:id], columns: [:author_name] }
}
end
Topic.find(topic.id).books.each do |book|
assert_equal new_author_name, book.author_name
end
end

it "updates nested associated objects" do
new_chapter_title = 'The Final Chapter'
book = new_topics.first.books.first
book.author_name = 'Richard Bachman'

example_chapter = book.chapters.first
example_chapter.title = new_chapter_title

assert_nothing_raised do
Topic.import new_topics,
recursive: true,
on_duplicate_key_update: [:id],
recursive_on_duplicate_key_update: {
books: { conflict_target: [:id], columns: [:author_name] },
chapters: { conflict_target: [:id], columns: [:title] }
}
end
assert_equal new_chapter_title, Chapter.find(example_chapter.id).title
end
end
end

# If returning option is provided, it is only applied to top level models so that SQL with invalid
Expand Down