mirror of https://github.com/rails/rails
Move `ActiveRecord::Base.insert` in `Relation`
Ref: https://github.com/rails/rails/pull/50396 As well as related methods.
This commit is contained in:
parent
8f419e482b
commit
ed2c15b524
|
@ -22,7 +22,7 @@ module ActiveRecord
|
|||
raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
|
||||
end
|
||||
|
||||
scoping { klass.#{method}(attributes, **kwargs) }
|
||||
super
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
|
|
@ -8,15 +8,16 @@ module ActiveRecord
|
|||
attr_reader :on_duplicate, :update_only, :returning, :unique_by, :update_sql
|
||||
|
||||
class << self
|
||||
def execute(model, ...)
|
||||
model.with_connection do |c|
|
||||
new(model, c, ...).execute
|
||||
def execute(relation, ...)
|
||||
relation.model.with_connection do |c|
|
||||
new(relation, c, ...).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(model, connection, inserts, on_duplicate:, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
||||
@model, @connection, @inserts = model, connection, inserts.map(&:stringify_keys)
|
||||
def initialize(relation, connection, inserts, on_duplicate:, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
||||
@relation = relation
|
||||
@model, @connection, @inserts = relation.model, connection, inserts.map(&:stringify_keys)
|
||||
@on_duplicate, @update_only, @returning, @unique_by = on_duplicate, update_only, returning, unique_by
|
||||
@record_timestamps = record_timestamps.nil? ? model.record_timestamps : record_timestamps
|
||||
|
||||
|
@ -31,10 +32,8 @@ module ActiveRecord
|
|||
@keys = @inserts.first.keys
|
||||
end
|
||||
|
||||
if model.scope_attributes?
|
||||
@scope_attributes = model.scope_attributes
|
||||
@keys |= @scope_attributes.keys
|
||||
end
|
||||
@scope_attributes = relation.scope_for_create.except(@model.inheritance_column)
|
||||
@keys |= @scope_attributes.keys
|
||||
@keys = @keys.to_set
|
||||
|
||||
@returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
|
||||
|
@ -74,7 +73,7 @@ module ActiveRecord
|
|||
def map_key_with_value
|
||||
inserts.map do |attributes|
|
||||
attributes = attributes.stringify_keys
|
||||
attributes.merge!(scope_attributes) if scope_attributes
|
||||
attributes.merge!(@scope_attributes)
|
||||
attributes.reverse_merge!(timestamps_for_create) if record_timestamps?
|
||||
|
||||
verify_attributes(attributes)
|
||||
|
@ -99,8 +98,6 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
private
|
||||
attr_reader :scope_attributes
|
||||
|
||||
def has_attribute_aliases?(attributes)
|
||||
attributes.keys.any? { |attribute| model.attribute_alias?(attribute) }
|
||||
end
|
||||
|
|
|
@ -87,282 +87,6 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
# Inserts a single record into the database in a single SQL INSERT
|
||||
# statement. It does not instantiate any models nor does it trigger
|
||||
# Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# See #insert_all for documentation.
|
||||
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
||||
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Inserts multiple records into the database in a single SQL INSERT
|
||||
# statement. It does not instantiate any models nor does it trigger
|
||||
# Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
||||
# the attributes for a single row and must have the same keys.
|
||||
#
|
||||
# Rows are considered to be unique by every unique index on the table. Any
|
||||
# duplicate rows are skipped.
|
||||
# Override with <tt>:unique_by</tt> (see below).
|
||||
#
|
||||
# Returns an ActiveRecord::Result with its contents based on
|
||||
# <tt>:returning</tt> (see below).
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# [:returning]
|
||||
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
||||
# inserted records, which by default is the primary key.
|
||||
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
||||
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
||||
# clause entirely.
|
||||
#
|
||||
# You can also pass an SQL string if you need more control on the return values
|
||||
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
||||
#
|
||||
# [:unique_by]
|
||||
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
||||
# by every unique index on the table. Any duplicate rows are skipped.
|
||||
#
|
||||
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
||||
#
|
||||
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
||||
# row has an existing id, or is not unique by another unique index,
|
||||
# ActiveRecord::RecordNotUnique is raised.
|
||||
#
|
||||
# Unique indexes can be identified by columns or name:
|
||||
#
|
||||
# unique_by: :isbn
|
||||
# unique_by: %i[ author_id name ]
|
||||
# unique_by: :index_books_on_isbn
|
||||
#
|
||||
# [:record_timestamps]
|
||||
# By default, automatic setting of timestamp columns is controlled by
|
||||
# the model's <tt>record_timestamps</tt> config, matching typical
|
||||
# behavior.
|
||||
#
|
||||
# To override this and force automatic setting of timestamp columns one
|
||||
# way or the other, pass <tt>:record_timestamps</tt>:
|
||||
#
|
||||
# record_timestamps: true # Always set timestamps automatically
|
||||
# record_timestamps: false # Never set timestamps automatically
|
||||
#
|
||||
# Because it relies on the index information from the database
|
||||
# <tt>:unique_by</tt> is recommended to be paired with
|
||||
# Active Record's schema_cache.
|
||||
#
|
||||
# ==== Example
|
||||
#
|
||||
# # Insert records and skip inserting any duplicates.
|
||||
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
||||
#
|
||||
# Book.insert_all([
|
||||
# { id: 1, title: "Rework", author: "David" },
|
||||
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
||||
# ])
|
||||
#
|
||||
# # insert_all works on chained scopes, and you can use create_with
|
||||
# # to set default attributes for all inserted records.
|
||||
#
|
||||
# author.books.create_with(created_at: Time.now).insert_all([
|
||||
# { id: 1, title: "Rework" },
|
||||
# { id: 2, title: "Eloquent Ruby" }
|
||||
# ])
|
||||
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
||||
InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Inserts a single record into the database in a single SQL INSERT
|
||||
# statement. It does not instantiate any models nor does it trigger
|
||||
# Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# See #insert_all! for more.
|
||||
def insert!(attributes, returning: nil, record_timestamps: nil)
|
||||
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Inserts multiple records into the database in a single SQL INSERT
|
||||
# statement. It does not instantiate any models nor does it trigger
|
||||
# Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
||||
# the attributes for a single row and must have the same keys.
|
||||
#
|
||||
# Raises ActiveRecord::RecordNotUnique if any rows violate a
|
||||
# unique index on the table. In that case, no rows are inserted.
|
||||
#
|
||||
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
||||
#
|
||||
# Returns an ActiveRecord::Result with its contents based on
|
||||
# <tt>:returning</tt> (see below).
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# [:returning]
|
||||
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
||||
# inserted records, which by default is the primary key.
|
||||
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
||||
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
||||
# clause entirely.
|
||||
#
|
||||
# You can also pass an SQL string if you need more control on the return values
|
||||
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
||||
#
|
||||
# [:record_timestamps]
|
||||
# By default, automatic setting of timestamp columns is controlled by
|
||||
# the model's <tt>record_timestamps</tt> config, matching typical
|
||||
# behavior.
|
||||
#
|
||||
# To override this and force automatic setting of timestamp columns one
|
||||
# way or the other, pass <tt>:record_timestamps</tt>:
|
||||
#
|
||||
# record_timestamps: true # Always set timestamps automatically
|
||||
# record_timestamps: false # Never set timestamps automatically
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
# # Insert multiple records
|
||||
# Book.insert_all!([
|
||||
# { title: "Rework", author: "David" },
|
||||
# { title: "Eloquent Ruby", author: "Russ" }
|
||||
# ])
|
||||
#
|
||||
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
||||
# # does not have a unique id.
|
||||
# Book.insert_all!([
|
||||
# { id: 1, title: "Rework", author: "David" },
|
||||
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
||||
# ])
|
||||
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
||||
InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Updates or inserts (upserts) a single record into the database in a
|
||||
# single SQL INSERT statement. It does not instantiate any models nor does
|
||||
# it trigger Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# See #upsert_all for documentation.
|
||||
def upsert(attributes, **kwargs)
|
||||
upsert_all([ attributes ], **kwargs)
|
||||
end
|
||||
|
||||
# Updates or inserts (upserts) multiple records into the database in a
|
||||
# single SQL INSERT statement. It does not instantiate any models nor does
|
||||
# it trigger Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
||||
# the attributes for a single row and must have the same keys.
|
||||
#
|
||||
# Returns an ActiveRecord::Result with its contents based on
|
||||
# <tt>:returning</tt> (see below).
|
||||
#
|
||||
# By default, +upsert_all+ will update all the columns that can be updated when
|
||||
# there is a conflict. These are all the columns except primary keys, read-only
|
||||
# columns, and columns covered by the optional +unique_by+.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# [:returning]
|
||||
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
||||
# inserted records, which by default is the primary key.
|
||||
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
||||
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
||||
# clause entirely.
|
||||
#
|
||||
# You can also pass an SQL string if you need more control on the return values
|
||||
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
||||
#
|
||||
# [:unique_by]
|
||||
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
||||
# by every unique index on the table. Any duplicate rows are skipped.
|
||||
#
|
||||
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
||||
#
|
||||
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
||||
# row has an existing id, or is not unique by another unique index,
|
||||
# ActiveRecord::RecordNotUnique is raised.
|
||||
#
|
||||
# Unique indexes can be identified by columns or name:
|
||||
#
|
||||
# unique_by: :isbn
|
||||
# unique_by: %i[ author_id name ]
|
||||
# unique_by: :index_books_on_isbn
|
||||
#
|
||||
# Because it relies on the index information from the database
|
||||
# <tt>:unique_by</tt> is recommended to be paired with
|
||||
# Active Record's schema_cache.
|
||||
#
|
||||
# [:on_duplicate]
|
||||
# Configure the SQL update sentence that will be used in case of conflict.
|
||||
#
|
||||
# NOTE: If you use this option you must provide all the columns you want to update
|
||||
# by yourself.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Commodity.upsert_all(
|
||||
# [
|
||||
# { id: 2, name: "Copper", price: 4.84 },
|
||||
# { id: 4, name: "Gold", price: 1380.87 },
|
||||
# { id: 6, name: "Aluminium", price: 0.35 }
|
||||
# ],
|
||||
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
||||
# )
|
||||
#
|
||||
# See the related +:update_only+ option. Both options can't be used at the same time.
|
||||
#
|
||||
# [:update_only]
|
||||
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
||||
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
||||
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Commodity.upsert_all(
|
||||
# [
|
||||
# { id: 2, name: "Copper", price: 4.84 },
|
||||
# { id: 4, name: "Gold", price: 1380.87 },
|
||||
# { id: 6, name: "Aluminium", price: 0.35 }
|
||||
# ],
|
||||
# update_only: [:price] # Only prices will be updated
|
||||
# )
|
||||
#
|
||||
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
||||
#
|
||||
# [:record_timestamps]
|
||||
# By default, automatic setting of timestamp columns is controlled by
|
||||
# the model's <tt>record_timestamps</tt> config, matching typical
|
||||
# behavior.
|
||||
#
|
||||
# To override this and force automatic setting of timestamp columns one
|
||||
# way or the other, pass <tt>:record_timestamps</tt>:
|
||||
#
|
||||
# record_timestamps: true # Always set timestamps automatically
|
||||
# record_timestamps: false # Never set timestamps automatically
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
||||
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
||||
#
|
||||
# Book.upsert_all([
|
||||
# { title: "Rework", author: "David", isbn: "1" },
|
||||
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
||||
# ], unique_by: :isbn)
|
||||
#
|
||||
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
||||
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
||||
InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Given an attributes hash, +instantiate+ returns a new instance of
|
||||
# the appropriate class. Accepts only keys as strings.
|
||||
#
|
||||
|
|
|
@ -19,6 +19,7 @@ module ActiveRecord
|
|||
:count, :average, :minimum, :maximum, :sum, :calculate,
|
||||
:pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with, :with_recursive,
|
||||
:async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
|
||||
:insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
|
||||
].freeze # :nodoc:
|
||||
delegate(*QUERYING_METHODS, to: :all)
|
||||
|
||||
|
|
|
@ -627,6 +627,283 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
# Inserts a single record into the database in a single SQL INSERT
|
||||
# statement. It does not instantiate any models nor does it trigger
|
||||
# Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# See #insert_all for documentation.
|
||||
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
||||
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Inserts multiple records into the database in a single SQL INSERT
|
||||
# statement. It does not instantiate any models nor does it trigger
|
||||
# Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
||||
# the attributes for a single row and must have the same keys.
|
||||
#
|
||||
# Rows are considered to be unique by every unique index on the table. Any
|
||||
# duplicate rows are skipped.
|
||||
# Override with <tt>:unique_by</tt> (see below).
|
||||
#
|
||||
# Returns an ActiveRecord::Result with its contents based on
|
||||
# <tt>:returning</tt> (see below).
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# [:returning]
|
||||
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
||||
# inserted records, which by default is the primary key.
|
||||
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
||||
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
||||
# clause entirely.
|
||||
#
|
||||
# You can also pass an SQL string if you need more control on the return values
|
||||
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
||||
#
|
||||
# [:unique_by]
|
||||
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
||||
# by every unique index on the table. Any duplicate rows are skipped.
|
||||
#
|
||||
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
||||
#
|
||||
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
||||
# row has an existing id, or is not unique by another unique index,
|
||||
# ActiveRecord::RecordNotUnique is raised.
|
||||
#
|
||||
# Unique indexes can be identified by columns or name:
|
||||
#
|
||||
# unique_by: :isbn
|
||||
# unique_by: %i[ author_id name ]
|
||||
# unique_by: :index_books_on_isbn
|
||||
#
|
||||
# [:record_timestamps]
|
||||
# By default, automatic setting of timestamp columns is controlled by
|
||||
# the model's <tt>record_timestamps</tt> config, matching typical
|
||||
# behavior.
|
||||
#
|
||||
# To override this and force automatic setting of timestamp columns one
|
||||
# way or the other, pass <tt>:record_timestamps</tt>:
|
||||
#
|
||||
# record_timestamps: true # Always set timestamps automatically
|
||||
# record_timestamps: false # Never set timestamps automatically
|
||||
#
|
||||
# Because it relies on the index information from the database
|
||||
# <tt>:unique_by</tt> is recommended to be paired with
|
||||
# Active Record's schema_cache.
|
||||
#
|
||||
# ==== Example
|
||||
#
|
||||
# # Insert records and skip inserting any duplicates.
|
||||
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
||||
#
|
||||
# Book.insert_all([
|
||||
# { id: 1, title: "Rework", author: "David" },
|
||||
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
||||
# ])
|
||||
#
|
||||
# # insert_all works on chained scopes, and you can use create_with
|
||||
# # to set default attributes for all inserted records.
|
||||
#
|
||||
# author.books.create_with(created_at: Time.now).insert_all([
|
||||
# { id: 1, title: "Rework" },
|
||||
# { id: 2, title: "Eloquent Ruby" }
|
||||
# ])
|
||||
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
||||
InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Inserts a single record into the database in a single SQL INSERT
|
||||
# statement. It does not instantiate any models nor does it trigger
|
||||
# Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# See #insert_all! for more.
|
||||
def insert!(attributes, returning: nil, record_timestamps: nil)
|
||||
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Inserts multiple records into the database in a single SQL INSERT
|
||||
# statement. It does not instantiate any models nor does it trigger
|
||||
# Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
||||
# the attributes for a single row and must have the same keys.
|
||||
#
|
||||
# Raises ActiveRecord::RecordNotUnique if any rows violate a
|
||||
# unique index on the table. In that case, no rows are inserted.
|
||||
#
|
||||
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
||||
#
|
||||
# Returns an ActiveRecord::Result with its contents based on
|
||||
# <tt>:returning</tt> (see below).
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# [:returning]
|
||||
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
||||
# inserted records, which by default is the primary key.
|
||||
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
||||
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
||||
# clause entirely.
|
||||
#
|
||||
# You can also pass an SQL string if you need more control on the return values
|
||||
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
||||
#
|
||||
# [:record_timestamps]
|
||||
# By default, automatic setting of timestamp columns is controlled by
|
||||
# the model's <tt>record_timestamps</tt> config, matching typical
|
||||
# behavior.
|
||||
#
|
||||
# To override this and force automatic setting of timestamp columns one
|
||||
# way or the other, pass <tt>:record_timestamps</tt>:
|
||||
#
|
||||
# record_timestamps: true # Always set timestamps automatically
|
||||
# record_timestamps: false # Never set timestamps automatically
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
# # Insert multiple records
|
||||
# Book.insert_all!([
|
||||
# { title: "Rework", author: "David" },
|
||||
# { title: "Eloquent Ruby", author: "Russ" }
|
||||
# ])
|
||||
#
|
||||
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
||||
# # does not have a unique id.
|
||||
# Book.insert_all!([
|
||||
# { id: 1, title: "Rework", author: "David" },
|
||||
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
||||
# ])
|
||||
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
||||
InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Updates or inserts (upserts) a single record into the database in a
|
||||
# single SQL INSERT statement. It does not instantiate any models nor does
|
||||
# it trigger Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# See #upsert_all for documentation.
|
||||
def upsert(attributes, **kwargs)
|
||||
upsert_all([ attributes ], **kwargs)
|
||||
end
|
||||
|
||||
# Updates or inserts (upserts) multiple records into the database in a
|
||||
# single SQL INSERT statement. It does not instantiate any models nor does
|
||||
# it trigger Active Record callbacks or validations. Though passed values
|
||||
# go through Active Record's type casting and serialization.
|
||||
#
|
||||
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
||||
# the attributes for a single row and must have the same keys.
|
||||
#
|
||||
# Returns an ActiveRecord::Result with its contents based on
|
||||
# <tt>:returning</tt> (see below).
|
||||
#
|
||||
# By default, +upsert_all+ will update all the columns that can be updated when
|
||||
# there is a conflict. These are all the columns except primary keys, read-only
|
||||
# columns, and columns covered by the optional +unique_by+.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# [:returning]
|
||||
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
||||
# inserted records, which by default is the primary key.
|
||||
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
||||
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
||||
# clause entirely.
|
||||
#
|
||||
# You can also pass an SQL string if you need more control on the return values
|
||||
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
||||
#
|
||||
# [:unique_by]
|
||||
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
||||
# by every unique index on the table. Any duplicate rows are skipped.
|
||||
#
|
||||
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
||||
#
|
||||
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
||||
# row has an existing id, or is not unique by another unique index,
|
||||
# ActiveRecord::RecordNotUnique is raised.
|
||||
#
|
||||
# Unique indexes can be identified by columns or name:
|
||||
#
|
||||
# unique_by: :isbn
|
||||
# unique_by: %i[ author_id name ]
|
||||
# unique_by: :index_books_on_isbn
|
||||
#
|
||||
# Because it relies on the index information from the database
|
||||
# <tt>:unique_by</tt> is recommended to be paired with
|
||||
# Active Record's schema_cache.
|
||||
#
|
||||
# [:on_duplicate]
|
||||
# Configure the SQL update sentence that will be used in case of conflict.
|
||||
#
|
||||
# NOTE: If you use this option you must provide all the columns you want to update
|
||||
# by yourself.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Commodity.upsert_all(
|
||||
# [
|
||||
# { id: 2, name: "Copper", price: 4.84 },
|
||||
# { id: 4, name: "Gold", price: 1380.87 },
|
||||
# { id: 6, name: "Aluminium", price: 0.35 }
|
||||
# ],
|
||||
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
||||
# )
|
||||
#
|
||||
# See the related +:update_only+ option. Both options can't be used at the same time.
|
||||
#
|
||||
# [:update_only]
|
||||
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
||||
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
||||
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Commodity.upsert_all(
|
||||
# [
|
||||
# { id: 2, name: "Copper", price: 4.84 },
|
||||
# { id: 4, name: "Gold", price: 1380.87 },
|
||||
# { id: 6, name: "Aluminium", price: 0.35 }
|
||||
# ],
|
||||
# update_only: [:price] # Only prices will be updated
|
||||
# )
|
||||
#
|
||||
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
||||
#
|
||||
# [:record_timestamps]
|
||||
# By default, automatic setting of timestamp columns is controlled by
|
||||
# the model's <tt>record_timestamps</tt> config, matching typical
|
||||
# behavior.
|
||||
#
|
||||
# To override this and force automatic setting of timestamp columns one
|
||||
# way or the other, pass <tt>:record_timestamps</tt>:
|
||||
#
|
||||
# record_timestamps: true # Always set timestamps automatically
|
||||
# record_timestamps: false # Never set timestamps automatically
|
||||
#
|
||||
# ==== Examples
|
||||
#
|
||||
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
||||
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
||||
#
|
||||
# Book.upsert_all([
|
||||
# { title: "Rework", author: "David", isbn: "1" },
|
||||
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
||||
# ], unique_by: :isbn)
|
||||
#
|
||||
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
||||
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
||||
InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
||||
end
|
||||
|
||||
# Updates the counters of the records in the current relation.
|
||||
#
|
||||
# ==== Parameters
|
||||
|
|
|
@ -67,7 +67,8 @@ module ActiveRecord
|
|||
:first_or_create, :first_or_create!, :first_or_initialize,
|
||||
:find_or_create_by, :find_or_create_by!, :find_or_initialize_by,
|
||||
:create_or_find_by, :create_or_find_by!,
|
||||
:destroy, :destroy_all, :delete, :delete_all, :update_all, :touch_all, :delete_by, :destroy_by
|
||||
:destroy, :destroy_all, :delete, :delete_all, :update_all, :touch_all, :delete_by, :destroy_by,
|
||||
:insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all,
|
||||
]
|
||||
|
||||
def test_delegate_querying_methods
|
||||
|
|
Loading…
Reference in New Issue