mirror of https://github.com/rails/rails
Merge pull request #45036 from fatkodima/insert_all-attribute-aliases
Allow using aliased attributes with `insert_all`/`upsert_all`
This commit is contained in:
commit
79b087ebda
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue