Merge pull request #45036 from fatkodima/insert_all-attribute-aliases

Allow using aliased attributes with `insert_all`/`upsert_all`
This commit is contained in:
Jean Boussier 2022-05-11 11:01:27 -04:00 committed by GitHub
commit 79b087ebda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 4 deletions

View File

@ -1,3 +1,15 @@
* Allow using aliased attributes with `insert_all`/`upsert_all`.
```ruby
class Book < ApplicationRecord
alias_attribute :title, :name
end
Book.insert_all [{ title: "Remote", author_id: 1 }], returning: :title
```
*fatkodima*
* Support encrypted attributes on columns with default db values.
This adds support for encrypted attributes defined on columns with default values.

View File

@ -10,15 +10,18 @@ module ActiveRecord
def initialize(model, inserts, on_duplicate:, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
@model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
@model, @connection, @inserts = model, model.connection, inserts
@on_duplicate, @update_only, @returning, @unique_by = on_duplicate, update_only, returning, unique_by
@record_timestamps = record_timestamps.nil? ? model.record_timestamps : record_timestamps
disallow_raw_sql!(on_duplicate)
disallow_raw_sql!(returning)
resolve_attribute_aliases
configure_on_duplicate_update_logic
@keys = @inserts.first.keys.map(&:to_s)
if model.scope_attributes?
@scope_attributes = model.scope_attributes
@keys |= @scope_attributes.keys
@ -28,7 +31,7 @@ module ActiveRecord
@returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
@returning = false if @returning == []
@unique_by = find_unique_index_for(unique_by)
@unique_by = find_unique_index_for(@unique_by)
@on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
ensure_valid_options_for_connection!
@ -88,6 +91,25 @@ module ActiveRecord
private
attr_reader :scope_attributes
def has_attribute_aliases?(attributes)
attributes.keys.any? { |attribute| model.attribute_alias?(attribute) }
end
def resolve_attribute_aliases
return unless has_attribute_aliases?(@inserts.first)
@inserts = @inserts.map do |insert|
insert.transform_keys { |attribute| resolve_attribute_alias(attribute) }
end
@update_only = Array(@update_only).map { |attribute| resolve_attribute_alias(attribute) } if @update_only
@unique_by = Array(@unique_by).map { |attribute| resolve_attribute_alias(attribute) } if @unique_by
end
def resolve_attribute_alias(attribute)
model.attribute_alias(attribute) || attribute
end
def configure_on_duplicate_update_logic
if custom_update_sql_provided? && update_only.present?
raise ArgumentError, "You can't set :update_only and provide custom update SQL via :on_duplicate at the same time"
@ -212,7 +234,13 @@ module ActiveRecord
if insert_all.returning.is_a?(String)
insert_all.returning
else
format_columns(insert_all.returning)
Array(insert_all.returning).map do |attribute|
if model.attribute_alias?(attribute)
"#{quote_column(model.attribute_alias(attribute))} AS #{quote_column(attribute)}"
else
quote_column(attribute)
end
end.join(",")
end
end
@ -271,7 +299,11 @@ module ActiveRecord
end
def quote_columns(columns)
columns.map(&connection.method(:quote_column_name))
columns.map { |column| quote_column(column) }
end
def quote_column(column)
connection.quote_column_name(column)
end
end
end

View File

@ -263,6 +263,25 @@ class InsertAllTest < ActiveRecord::TestCase
end
end
def test_insert_all_and_upsert_all_with_aliased_attributes
if supports_insert_returning?
assert_difference "Book.count" do
result = Book.insert_all [{ title: "Remote", author_id: 1 }], returning: :title
assert_includes result.columns, "title"
end
end
if supports_insert_on_duplicate_update?
Book.upsert_all [{ id: 101, title: "Perelandra", author_id: 7, isbn: "1974522598" }]
Book.upsert_all [{ id: 101, title: "Perelandra 2", author_id: 6, isbn: "111111" }], update_only: %i[ title isbn ]
book = Book.find(101)
assert_equal "Perelandra 2", book.title, "Should have updated the title"
assert_equal "111111", book.isbn, "Should have updated the isbn"
assert_equal 7, book.author_id, "Should not have updated the author_id"
end
end
def test_upsert_logs_message_including_model_name
skip unless supports_insert_on_duplicate_update?

View File

@ -11,6 +11,8 @@ class Book < ActiveRecord::Base
has_one :essay
alias_attribute :title, :name
enum status: [:proposed, :written, :published]
enum last_read: { unread: 0, reading: 2, read: 3, forgotten: nil }
enum nullable_status: [:single, :married]