123456789_123456789_123456789_123456789_123456789_

Module: ActiveRecord::Persistence::ClassMethods

Relationships & Source Files
Defined in: activerecord/lib/active_record/persistence.rb

Instance Attribute Summary

Instance Method Summary

Instance Attribute Details

#has_query_constraints?Boolean (readonly)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 495

def has_query_constraints? # :nodoc:
  @has_query_constraints
end

Instance Method Details

#_delete_record(constraints)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 611

def _delete_record(constraints) # :nodoc:
  constraints = constraints.map { |name, value| predicate_builder[name, value] }

  default_constraint = build_default_constraint
  constraints << default_constraint if default_constraint

  if current_scope = self.global_current_scope
    constraints << current_scope.where_clause.ast
  end

  dm = Arel::DeleteManager.new(arel_table)
  dm.wheres = constraints

  connection.delete(dm, "#{self} Destroy")
end

#_insert_record(values, returning)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 569

def _insert_record(values, returning) # :nodoc:
  primary_key = self.primary_key
  primary_key_value = nil

  if prefetch_primary_key? && primary_key
    values[primary_key] ||= begin
      primary_key_value = next_sequence_value
      _default_attributes[primary_key].with_cast_value(primary_key_value)
    end
  end

  im = Arel::InsertManager.new(arel_table)

  if values.empty?
    im.insert(connection.empty_insert_statement_value(primary_key))
  else
    im.insert(values.transform_keys { |name| arel_table[name] })
  end

  connection.insert(
    im, "#{self} Create", primary_key || false, primary_key_value,
    returning: returning
  )
end

#_update_record(values, constraints)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 594

def _update_record(values, constraints) # :nodoc:
  constraints = constraints.map { |name, value| predicate_builder[name, value] }

  default_constraint = build_default_constraint
  constraints << default_constraint if default_constraint

  if current_scope = self.global_current_scope
    constraints << current_scope.where_clause.ast
  end

  um = Arel::UpdateManager.new(arel_table)
  um.set(values.transform_keys { |name| arel_table[name] })
  um.wheres = constraints

  connection.update(um, "#{self} Update")
end

#build(attributes = nil, &block)

Builds an object (or multiple objects) and returns either the built object or a list of built objects.

The attributes parameter can be either a ::Hash or an ::Array of Hashes. These Hashes describe the attributes on the objects that are to be built.

Examples

# Build a single new object
User.build(first_name: 'Jamie')

# Build an Array of new objects
User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])

# Build a single object and pass it into a block to set other attributes.
User.build(first_name: 'Jamie') do |u|
  u.is_admin = false
end

# Building an Array of new objects using a block, where the block is executed for each object:
User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
  u.is_admin = false
end
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 82

def build(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| build(attr, &block) }
  else
    new(attributes, &block)
  end
end

#build_default_constraint (private)

Called by #_update_record and #_delete_record to build where clause from default scopes. Skips empty scopes.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 655

def build_default_constraint
  return unless default_scopes?(all_queries: true)

  default_where_clause = default_scoped(all_queries: true).where_clause
  default_where_clause.ast unless default_where_clause.empty?
end

#composite_query_constraints_list

This method is for internal use only.

Returns an array of column names to be used in queries. The source of column names is derived from #query_constraints_list or primary_key. This method is for internal use when the primary key is to be treated as an array.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 510

def composite_query_constraints_list # :nodoc:
  @composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
end

#create(attributes = nil, &block)

Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.

The attributes parameter can be either a ::Hash or an ::Array of Hashes. These Hashes describe the attributes on the objects that are to be created.

Examples

# Create a single new object
User.create(first_name: 'Jamie')

# Create an Array of new objects
User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])

# Create a single object and pass it into a block to set other attributes.
User.create(first_name: 'Jamie') do |u|
  u.is_admin = false
end

# Creating an Array of new objects using a block, where the block is executed for each object:
User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
  u.is_admin = false
end
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 33

def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    object = new(attributes, &block)
    object.save
    object
  end
end

#create!(attributes = nil, &block)

Creates an object (or multiple objects) and saves it to the database, if validations pass. Raises a ::ActiveRecord::RecordInvalid error if validations fail, unlike Base#create.

The attributes parameter can be either a ::Hash or an ::Array of Hashes. These describe which attributes to be created on the object, or multiple objects when given an ::Array of Hashes.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 50

def create!(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create!(attr, &block) }
  else
    object = new(attributes, &block)
    object.save!
    object
  end
end

#delete(id_or_array)

Deletes the row with a primary key matching the id argument, using an SQL DELETE statement, and returns the number of rows deleted. Active Record objects are not instantiated, so the object’s callbacks are not executed, including any :dependent association options.

You can delete multiple rows at once by passing an ::Array of ids.

Note: Although it is often much faster than the alternative, #destroy, skipping callbacks might bypass business logic in your application that ensures referential integrity or performs other essential jobs.

Examples

# Delete a single row
Todo.delete(1)

# Delete multiple rows
Todo.delete([2,3,4])
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 565

def delete(id_or_array)
  delete_by(primary_key => id_or_array)
end

#destroy(id)

Destroy an object (or multiple objects) that has the given id. The object is instantiated first, therefore all callbacks and filters are fired off before the object is deleted. This method is less efficient than #delete but allows cleanup methods and other actions to be run.

This essentially finds the object (or multiple objects) with the given id, creates a new object from the attributes, and then calls destroy on it.

Parameters

  • id - This should be the id or an array of ids to be destroyed.

Examples

# Destroy a single object
Todo.destroy(1)

# Destroy multiple objects
todos = [1,2,3]
Todo.destroy(todos)
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 533

def destroy(id)
  multiple_ids = if composite_primary_key?
    id.first.is_a?(Array)
  else
    id.is_a?(Array)
  end

  if multiple_ids
    find(id).each(&:destroy)
  else
    find(id).destroy
  end
end

#discriminate_class_for_record(record) (private)

Called by #instantiate to decide which class to use for a new record instance.

See ActiveRecord::Inheritance#discriminate_class_for_record for the single-table inheritance discriminator.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 648

def discriminate_class_for_record(record)
  self
end

#inherited(subclass) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 628

def inherited(subclass)
  super
  subclass.class_eval do
    @_query_constraints_list = nil
    @has_query_constraints = false
  end
end

#insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)

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.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 96

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

#insert!(attributes, returning: nil, record_timestamps: nil)

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.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 185

def insert!(attributes, returning: nil, record_timestamps: nil)
  insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
end

#insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)

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 :unique_by (see below).

Returns an ::ActiveRecord::Result with its contents based on :returning (see below).

Options

:returning

(PostgreSQL and SQLite3 only) An array of attributes to return for all successfully inserted records, which by default is the primary key. Pass returning: %w[ id name ] for both id and name or returning: false to omit the underlying RETURNING SQL clause entirely.

You can also pass an SQL string if you need more control on the return values (for example, returning: Arel.sql("id, name as new_name")).

: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 :unique_by.

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 record_timestamps config, matching typical behavior.

To override this and force automatic setting of timestamp columns one way or the other, pass :record_timestamps:

record_timestamps: true  # Always set timestamps automatically
record_timestamps: false # Never set timestamps automatically

Because it relies on the index information from the database :unique_by 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" }
])
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 175

def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
  InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
end

#insert_all!(attributes, returning: nil, record_timestamps: nil)

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 :returning (see below).

Options

:returning

(PostgreSQL and SQLite3 only) An array of attributes to return for all successfully inserted records, which by default is the primary key. Pass returning: %w[ id name ] for both id and name or returning: false to omit the underlying RETURNING SQL clause entirely.

You can also pass an SQL string if you need more control on the return values (for example, returning: Arel.sql("id, name as new_name")).

:record_timestamps

By default, automatic setting of timestamp columns is controlled by the model’s record_timestamps config, matching typical behavior.

To override this and force automatic setting of timestamp columns one way or the other, pass :record_timestamps:

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" }
])
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 242

def insert_all!(attributes, returning: nil, record_timestamps: nil)
  InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
end

#instantiate(attributes, column_types = {}, &block)

Given an attributes hash, instantiate returns a new instance of the appropriate class. Accepts only keys as strings.

For example, Post.all may return Comments, Messages, and Emails by storing the record’s subclass in a type attribute. By calling instantiate instead of new, finder methods ensure they get new instances of the appropriate class for each record.

See ActiveRecord::Inheritance#discriminate_class_for_record to see how this “single-table” inheritance mapping is implemented.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 376

def instantiate(attributes, column_types = {}, &block)
  klass = discriminate_class_for_record(attributes)
  instantiate_instance_of(klass, attributes, column_types, &block)
end

#instantiate_instance_of(klass, attributes, column_types = {}, &block) (private)

Given a class, an attributes hash, instantiate_instance_of returns a new instance of the class. Accepts only keys as strings.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 638

def instantiate_instance_of(klass, attributes, column_types = {}, &block)
  attributes = klass.attributes_builder.build_from_database(attributes, column_types)
  klass.allocate.init_with_attributes(attributes, &block)
end

#query_constraints(*columns_list)

Accepts a list of attribute names to be used in the WHERE clause of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for #first and #last finder methods.

class Developer < ActiveRecord::Base
  query_constraints :company_id, :id
end

developer = Developer.first
# SELECT "developers".* FROM "developers" ORDER BY "developers"."company_id" ASC, "developers"."id" ASC LIMIT 1
developer.inspect # => #<Developer id: 1, company_id: 1, ...>

developer.update!(name: "Nikita")
# UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1

# It is possible to update an attribute used in the query_constraints clause:
developer.update!(company_id: 2)
# UPDATE "developers" SET "company_id" = 2 WHERE "developers"."company_id" = 1 AND "developers"."id" = 1

developer.name = "Bob"
developer.save!
# UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1

developer.destroy!
# DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1

developer.delete
# DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1

developer.reload
# SELECT "developers".* FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1 LIMIT 1

Raises:

  • (ArgumentError)
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 488

def query_constraints(*columns_list)
  raise ArgumentError, "You must specify at least one column to be used in querying" if columns_list.empty?

  @query_constraints_list = columns_list.map(&:to_s)
  @has_query_constraints = @query_constraints_list
end

#query_constraints_list

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 499

def query_constraints_list # :nodoc:
  @query_constraints_list ||= if base_class? || primary_key != base_class.primary_key
    primary_key if primary_key.is_a?(Array)
  else
    base_class.query_constraints_list
  end
end

#update(id = :all, attributes)

Updates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.

Parameters

  • id - This should be the id or an array of ids to be updated. Optional argument, defaults to all records in the relation.

  • attributes - This should be a hash of attributes or an array of hashes.

Examples

# Updates one record
Person.update(15, user_name: "Samuel", group: "expert")

# Updates multiple records
people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
Person.update(people.keys, people.values)

# Updates multiple records from the result of a relation
people = Person.where(group: "expert")
people.update(group: "masters")

Note: Updating a large number of records will run an UPDATE query for each record, which may cause a performance issue. When running callbacks is not needed for each record update, it is preferred to use update_all for updating all records in a single query.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 408

def update(id = :all, attributes)
  if id.is_a?(Array)
    if id.any?(ActiveRecord::Base)
      raise ArgumentError,
        "You are passing an array of ActiveRecord::Base instances to `update`. " \
        "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
    end
    id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
      object.update(attributes[idx])
    }
  elsif id == :all
    all.each { |record| record.update(attributes) }
  else
    if ActiveRecord::Base === id
      raise ArgumentError,
        "You are passing an instance of ActiveRecord::Base to `update`. " \
        "Please pass the id of the object by calling `.id`."
    end
    object = find(id)
    object.update(attributes)
    object
  end
end

#update!(id = :all, attributes)

Updates the object (or multiple objects) just like #update but calls #update! instead of #update, so an exception is raised if the record is invalid and saving will fail.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 434

def update!(id = :all, attributes)
  if id.is_a?(Array)
    if id.any?(ActiveRecord::Base)
      raise ArgumentError,
        "You are passing an array of ActiveRecord::Base instances to `update!`. " \
        "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
    end
    id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
      object.update!(attributes[idx])
    }
  elsif id == :all
    all.each { |record| record.update!(attributes) }
  else
    if ActiveRecord::Base === id
      raise ArgumentError,
        "You are passing an instance of ActiveRecord::Base to `update!`. " \
        "Please pass the id of the object by calling `.id`."
    end
    object = find(id)
    object.update!(attributes)
    object
  end
end

#upsert(attributes, **kwargs)

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.

[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 252

def upsert(attributes, **kwargs)
  upsert_all([ attributes ], **kwargs)
end

#upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)

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 :returning (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 and SQLite3 only) An array of attributes to return for all successfully inserted records, which by default is the primary key. Pass returning: %w[ id name ] for both id and name or returning: false to omit the underlying RETURNING SQL clause entirely.

You can also pass an SQL string if you need more control on the return values (for example, returning: Arel.sql("id, name as new_name")).

: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 :unique_by.

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 :unique_by 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 record_timestamps config, matching typical behavior.

To override this and force automatic setting of timestamp columns one way or the other, pass :record_timestamps:

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"
[ GitHub ]

  
# File 'activerecord/lib/active_record/persistence.rb', line 362

def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
  InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
end