跳到內容 跳到搜尋

Active Record 關聯

命名空間
方法
#
A
B
C
D
E
F
I
J
L
M
N
O
P
R
S
T
U
V
包含的模組

常數

CLAUSE_METHODS = [:where, :having, :from]
 
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :with]
 
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group, :order, :joins, :left_outer_joins, :references, :extending, :unscope, :optimizer_hints, :annotate, :with]
 
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, :strict_loading, :reverse_order, :distinct, :create_with, :skip_query_cache]
 
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
 

屬性

[R] klass
[R] loaded
[R] loaded?
[R] model
[R] predicate_builder
[RW] skip_preloading_value
[R] table

類別公開方法

new(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})

# File activerecord/lib/active_record/relation.rb, line 28
def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
  @klass  = klass
  @table  = table
  @values = values
  @loaded = false
  @predicate_builder = predicate_builder
  @delegate_to_klass = false
  @future_result = nil
  @records = nil
  @async = false
  @none = false
end

實例公開方法

==(other)

比較兩個關聯的等號。

# File activerecord/lib/active_record/relation.rb, line 813
def ==(other)
  case other
  when Associations::CollectionProxy, AssociationRelation
    self == other.records
  when Relation
    other.to_sql == to_sql
  when Array
    records == other
  end
end

any?(*args)

如果任何記錄存在,則傳回 true。

當提供樣式引數時,此方法會透過大小寫相等運算子 (===) 檢查 Enumerable 中的元素是否符合樣式。

posts.any?(Post) # => true or false
# File activerecord/lib/active_record/relation.rb, line 312
def any?(*args)
  return false if @none

  return super if args.present? || block_given?
  !empty?
end

blank?()

如果關聯為空白,則傳回 true。

# File activerecord/lib/active_record/relation.rb, line 834
def blank?
  records.blank?
end

build(attributes = nil, &block)

別名:new

cache_key(timestamp_column = "updated_at")

傳回一個穩定的快取金鑰,可用於識別此查詢。快取金鑰是使用 SQL 查詢的指紋建立的。

Product.where("name like ?", "%Cosmic Encounter%").cache_key
# => "products/query-1850ab3d302391b85b8693e941286659"

如果 ActiveRecord::Base.collection_cache_versioning 已關閉(就像在 Rails 6.0 和更早版本中一樣),快取金鑰也會包含一個版本。

ActiveRecord::Base.collection_cache_versioning = false
Product.where("name like ?", "%Cosmic Encounter%").cache_key
# => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"

您也可以傳遞自訂時間戳記欄位來擷取最後更新記錄的時間戳記。

Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
# File activerecord/lib/active_record/relation.rb, line 359
def cache_key(timestamp_column = "updated_at")
  @cache_keys ||= {}
  @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
end

cache_key_with_version()

傳回快取金鑰和版本。

# File activerecord/lib/active_record/relation.rb, line 438
def cache_key_with_version
  if version = cache_version
    "#{cache_key}-#{version}"
  else
    cache_key
  end
end

cache_version(timestamp_column = :updated_at)

傳回快取版本,可與快取金鑰一起使用以形成可回收的快取機制。快取版本會使用符合查詢的記錄數目和最後更新記錄的時間戳記來建置。當有新的記錄符合查詢,或任何現有記錄更新或刪除時,快取版本會變更。

如果已載入集合,此方法會反覆處理記錄以產生時間戳記,否則會觸發一個 SQL 查詢,如下所示

SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
# File activerecord/lib/active_record/relation.rb, line 386
def cache_version(timestamp_column = :updated_at)
  if collection_cache_versioning
    @cache_versions ||= {}
    @cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
  end
end

create(attributes = nil, &block)

嘗試使用關係中定義的相同範圍屬性建立新的記錄。如果驗證失敗,傳回已初始化的物件。

預期參數格式與 ActiveRecord::Base.create 相同。

範例

users = User.where(name: 'Oscar')
users.create # => #<User id: 3, name: "Oscar", ...>

users.create(name: 'fxn')
users.create # => #<User id: 4, name: "fxn", ...>

users.create { |user| user.name = 'tenderlove' }
# => #<User id: 5, name: "tenderlove", ...>

users.create(name: nil) # validation on name
# => #<User id: nil, name: nil, ...>
# File activerecord/lib/active_record/relation.rb, line 98
def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    block = current_scope_restoring_block(&block)
    scoping { _create(attributes, &block) }
  end
end

create!(attributes = nil, &block)

類似於 create,但會在基本類別上呼叫 create!。如果發生驗證錯誤,會引發例外狀況。

預期參數格式與 ActiveRecord::Base.create! 相同。

# File activerecord/lib/active_record/relation.rb, line 113
def create!(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create!(attr, &block) }
  else
    block = current_scope_restoring_block(&block)
    scoping { _create!(attributes, &block) }
  end
end

create_or_find_by(屬性, &區塊)

嘗試在資料表中建立一筆具有指定屬性的記錄,該資料表在其一或多個欄位上具有唯一的資料庫約束。如果已存在一行具有其中一個或多個這些唯一約束,則會捕捉到此類插入通常會引發的例外狀況,並使用 find_by! 找到具有這些屬性的現有記錄。

這類似於 find_or_create_by,但會先嘗試建立記錄。因此,它更適合於記錄很可能尚未存在的案例。

不過,create_or_find_by 有幾個缺點

  • 基礎資料表必須定義具有唯一資料庫約束的相關欄位。

  • 唯一約束違規可能僅由一個或至少少於所有指定的屬性觸發。這表示後續的 find_by! 可能無法找到相符的記錄,這將引發 ActiveRecord::RecordNotFound 例外狀況,而不是具有指定屬性的記錄。

  • 雖然我們避免了 find_or_create_by 中 SELECT -> INSERT 之間的競爭條件,但我們實際上在 INSERT -> SELECT 之間有另一個競爭條件,如果另一個用戶端在兩個陳述之間執行 DELETE,則可能會觸發此條件。但對於大多數應用程式而言,這是一個不太可能發生的條件。

  • 它依賴例外狀況處理來處理控制流程,這可能會稍微慢一些。

  • 主鍵可能會在每次建立時自動遞增,即使失敗也是如此。如果基礎資料表仍停留在 int 類型的主鍵上,這可能會加速用盡整數的問題(注意:自 5.1+ 以來的所有 Rails 應用程式都預設為 bigint,不會出現此問題)。

如果所有指定的屬性都涵蓋在唯一約束中,此方法將傳回一筆記錄(除非觸發 INSERT -> DELETE -> SELECT 競爭條件),但如果嘗試建立並因驗證錯誤而失敗,它將不會持續存在,您會在這種情況下取得 create 傳回的內容。

# File activerecord/lib/active_record/relation.rb, line 215
def create_or_find_by(attributes, &block)
  transaction(requires_new: true) { create(attributes, &block) }
rescue ActiveRecord::RecordNotUnique
  if connection.transaction_open?
    where(attributes).lock.find_by!(attributes)
  else
    find_by!(attributes)
  end
end

create_or_find_by!(attributes, &block)

就像 create_or_find_by,但呼叫 create!,所以如果建立的記錄無效,會引發例外。

# File activerecord/lib/active_record/relation.rb, line 228
def create_or_find_by!(attributes, &block)
  transaction(requires_new: true) { create!(attributes, &block) }
rescue ActiveRecord::RecordNotUnique
  if connection.transaction_open?
    where(attributes).lock.find_by!(attributes)
  else
    find_by!(attributes)
  end
end

delete_all()

刪除記錄,而不會先實例化記錄,因此不會呼叫 #destroy 方法,也不會呼叫回呼。這是一個單一的 SQL DELETE 陳述式,直接進入資料庫,比 destroy_all 有效率得多。不過要小心關聯,特別是關聯上定義的 :dependent 規則不會被遵守。傳回受影響列數。

Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all

這兩個呼叫都會使用單一的 DELETE 陳述式一次刪除所有受影響的貼文。如果你需要刪除依賴的關聯或呼叫你的 before_*after_destroy 回呼,請改用 destroy_all 方法。

如果提供無效的方法,delete_all 會引發 ActiveRecordError

Post.distinct.delete_all
# => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
# File activerecord/lib/active_record/relation.rb, line 646
def delete_all
  return 0 if @none

  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
    value = @values[method]
    method == :distinct ? value : value&.any?
  end
  if invalid_methods.any?
    raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
  end

  arel = eager_loading? ? apply_join_dependency.arel : build_arel
  arel.source.left = table

  group_values_arel_columns = arel_columns(group_values.uniq)
  having_clause_ast = having_clause.ast unless having_clause.empty?
  stmt = arel.compile_delete(table[primary_key], having_clause_ast, group_values_arel_columns)

  klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
end

delete_by(*args)

尋找並刪除所有符合指定條件的記錄。這是 relation.where(condition).delete_all 的簡寫。傳回受影響列數。

如果找不到記錄,傳回 0,因為沒有列受到影響。

Person.delete_by(id: 13)
Person.delete_by(name: 'Spartacus', rating: 4)
Person.delete_by("published_at < ?", 2.weeks.ago)
# File activerecord/lib/active_record/relation.rb, line 689
def delete_by(*args)
  where(*args).delete_all
end

destroy_all()

透過實例化每個記錄並呼叫其 #destroy 方法來刪除記錄。每個物件的回呼都會被執行(包括 :dependent 關聯選項)。傳回被刪除的物件集合;每個物件都會被凍結,以反映不應該做任何變更(因為它們無法被持續化)。

注意:當你一次移除多個記錄時,實例化、執行回呼和刪除每個記錄可能會很耗時。它會為每個記錄產生至少一個 SQL DELETE 查詢(或更多,以強制你的回呼)。如果你想快速刪除多列,而不考慮它們的關聯或回呼,請改用 delete_all

範例

Person.where(age: 0..18).destroy_all
# File activerecord/lib/active_record/relation.rb, line 624
def destroy_all
  records.each(&:destroy).tap { reset }
end

destroy_by(*args)

尋找並刪除符合指定條件的所有記錄。這是 relation.where(condition).destroy_all 的簡寫。傳回已刪除的物件集合。

如果找不到任何記錄,傳回空陣列。

Person.destroy_by(id: 13)
Person.destroy_by(name: 'Spartacus', rating: 4)
Person.destroy_by("published_at < ?", 2.weeks.ago)
# File activerecord/lib/active_record/relation.rb, line 676
def destroy_by(*args)
  where(*args).destroy_all
end

eager_loading?()

如果關聯需要急切載入,傳回 true。

# File activerecord/lib/active_record/relation.rb, line 798
def eager_loading?
  @should_eager_load ||=
    eager_load_values.any? ||
    includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
end

empty?()

如果沒有任何記錄,傳回 true。

# File activerecord/lib/active_record/relation.rb, line 283
def empty?
  return true if @none

  if loaded?
    records.empty?
  else
    !exists?
  end
end

encode_with(coder)

序列化關聯物件 Array

# File activerecord/lib/active_record/relation.rb, line 269
def encode_with(coder)
  coder.represent_seq(nil, records)
end

explain(*options)

對由這個關聯觸發的查詢或查詢執行 EXPLAIN,並傳回結果字串。字串的格式會模仿資料庫 shell 印出的格式。

請注意,這個方法實際上會執行查詢,因為當急切載入進行中時,某些查詢的結果會被後續查詢需要。

請參閱 Active Record 查詢介面指南 中的進一步詳細資訊。

# File activerecord/lib/active_record/relation.rb, line 253
def explain(*options)
  exec_explain(collecting_queries_for_explain { exec_queries }, options)
end

find_or_create_by(屬性, &區塊)

尋找具有給定屬性的第一個記錄,或在找不到時建立一個具有該屬性的記錄

# Find the first user named "Penélope" or create a new one.
User.find_or_create_by(first_name: 'Penélope')
# => #<User id: 1, first_name: "Penélope", last_name: nil>

# Find the first user named "Penélope" or create a new one.
# We already have one so the existing record will be returned.
User.find_or_create_by(first_name: 'Penélope')
# => #<User id: 1, first_name: "Penélope", last_name: nil>

# Find the first user named "Scarlett" or create a new one with
# a particular last name.
User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
# => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">

這個方法接受一個區塊,傳遞給 create。上面的最後一個範例可以改寫成這樣

# Find the first user named "Scarlett" or create a new one with a
# particular last name.
User.find_or_create_by(first_name: 'Scarlett') do |user|
  user.last_name = 'Johansson'
end
# => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">

這個方法總是會傳回一個記錄,但如果建立嘗試因為驗證錯誤而失敗,它不會被持續,你會得到 create 在這種情況下傳回的內容。

如果建立因為唯一約束而失敗,這個方法會假設它遇到競爭條件,並會再試一次尋找記錄。如果第二次尋找仍然找不到記錄,因為同時發生 DELETE,它會引發 ActiveRecord::RecordNotFound 例外。

請注意這個方法不是原子性的,它會先執行 SELECT,如果沒有結果,會嘗試 INSERT。所以如果表格沒有相關的唯一約束,你可能會得到兩個或更多相似的記錄。

# File activerecord/lib/active_record/relation.rb, line 175
def find_or_create_by(attributes, &block)
  find_by(attributes) || create_or_find_by(attributes, &block)
end

find_or_create_by!(屬性, &區塊)

類似 find_or_create_by,但呼叫 create!,因此如果建立的記錄無效,會引發例外。

# File activerecord/lib/active_record/relation.rb, line 182
def find_or_create_by!(attributes, &block)
  find_by(attributes) || create_or_find_by!(attributes, &block)
end

find_or_initialize_by(屬性, &區塊)

類似 find_or_create_by,但呼叫 new 而不是 create

# File activerecord/lib/active_record/relation.rb, line 240
def find_or_initialize_by(attributes, &block)
  find_by(attributes) || new(attributes, &block)
end

initialize_copy(其他)

# File activerecord/lib/active_record/relation.rb, line 41
def initialize_copy(other)
  @values = @values.dup
  reset
end

inspect()

# File activerecord/lib/active_record/relation.rb, line 846
def inspect
  subject = loaded? ? records : annotate("loading for inspect")
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)

  entries[10] = "..." if entries.size == 11

  "#<#{self.class.name} [#{entries.join(', ')}]>"
end

joined_includes_values()

也標記為預先載入的聯結。在這種情況下,我們應該直接載入它們。請注意,這是一個天真的實作,因為我們可能有代表相同關聯的字串和符號,但無法透過這個匹配。此外,我們可能有部分匹配的巢狀雜湊,例如 { a: :b } & { a: [:b, :c] }

# File activerecord/lib/active_record/relation.rb, line 808
def joined_includes_values
  includes_values & joins_values
end

load(&block)

如果記錄尚未載入,則會從資料庫載入記錄。如果您需要在實際使用記錄之前明確載入一些記錄,則可以使用此方法。傳回值是關聯本身,而不是記錄。

Post.where(published: true).load # => #<ActiveRecord::Relation>
# File activerecord/lib/active_record/relation.rb, line 740
def load(&block)
  if !loaded? || scheduled?
    @records = exec_queries(&block)
    @loaded = true
  end

  self
end

load_async()

排程查詢,從背景執行緒池執行。

Post.where(published: true).load_async # => #<ActiveRecord::Relation>

當反覆運算 Relation 時,如果背景查詢尚未執行,則會由前景執行緒執行。

請注意,必須設定 config.active_record.async_query_executor,才能實際並行執行查詢。否則,它會預設在前台執行查詢。

當啟用交易固定裝置時,load_async 也會在測試環境中回退到在前台執行。

如果查詢實際上是在背景中執行的,則 Active Record 記錄會透過在記錄行之前加上 ASYNC 前綴來顯示它

ASYNC Post Load (0.0ms) (db time 2ms)  SELECT "posts".* FROM "posts" LIMIT 100
# File activerecord/lib/active_record/relation.rb, line 711
def load_async
  return load if !connection.async_enabled?

  unless loaded?
    result = exec_main_query(async: connection.current_transaction.closed?)

    if result.is_a?(Array)
      @records = result
    else
      @future_result = result
    end
    @loaded = true
  end

  self
end

many?()

如果有多個記錄,則傳回 true。

# File activerecord/lib/active_record/relation.rb, line 334
def many?
  return false if @none

  return super if block_given?
  return records.many? if loaded?
  limited_count > 1
end

new(attributes = nil, &block)

在關係中初始化新的記錄,同時維護目前的範圍。

預期的參數格式與 ActiveRecord::Base.new 相同。

users = User.where(name: 'DHH')
user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>

您也可以將區塊傳遞給 new,並將新記錄作為參數

user = users.new { |user| user.name = 'Oscar' }
user.name # => Oscar
別名為: build
# File activerecord/lib/active_record/relation.rb, line 69
def new(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| new(attr, &block) }
  else
    block = current_scope_restoring_block(&block)
    scoping { _new(attributes, &block) }
  end
end

none?(*args)

如果沒有任何記錄,傳回 true。

當提供樣式引數時,此方法會透過大小寫相等運算子 (===) 檢查 Enumerable 中的元素是否符合樣式。

posts.none?(Comment) # => true or false
# File activerecord/lib/active_record/relation.rb, line 299
def none?(*args)
  return true if @none

  return super if args.present? || block_given?
  empty?
end

one?(*args)

如果只有一筆記錄,則傳回 true。

當提供樣式引數時,此方法會透過大小寫相等運算子 (===) 檢查 Enumerable 中的元素是否符合樣式。

posts.one?(Post) # => true or false
# File activerecord/lib/active_record/relation.rb, line 325
def one?(*args)
  return false if @none

  return super if args.present? || block_given?
  return records.one? if loaded?
  limited_count == 1
end

pretty_print(pp)

# File activerecord/lib/active_record/relation.rb, line 824
def pretty_print(pp)
  subject = loaded? ? records : annotate("loading for pp")
  entries = subject.take([limit_value, 11].compact.min)

  entries[10] = "..." if entries.size == 11

  pp.pp(entries)
end

reload()

強制重新載入關聯。

# File activerecord/lib/active_record/relation.rb, line 750
def reload
  reset
  load
end

reset()

# File activerecord/lib/active_record/relation.rb, line 755
def reset
  @future_result&.cancel
  @future_result = nil
  @delegate_to_klass = false
  @to_sql = @arel = @loaded = @should_eager_load = nil
  @offsets = @take = nil
  @cache_keys = nil
  @cache_versions = nil
  @records = nil
  self
end

scheduled?()

如果關聯已排程在背景執行緒池中,則傳回 true

# File activerecord/lib/active_record/relation.rb, line 730
def scheduled?
  !!@future_result
end

scope_for_create()

# File activerecord/lib/active_record/relation.rb, line 791
def scope_for_create
  hash = where_clause.to_h(klass.table_name, equality_only: true)
  create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
  hash
end

scoping(all_queries: nil, &block)

將所有查詢範圍設定為目前範圍。

Comment.where(post_id: 1).scoping do
  Comment.first
end
# SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1

如果傳遞 all_queries: true,範圍設定將套用至關聯的所有查詢,包括實例上的 updatedelete。一旦 all_queries 設為 true,便無法在巢狀區塊中將其設為 false。

如果您要在執行區塊期間移除所有先前的範圍(包括 default_scope),請檢查 unscoped。

# File activerecord/lib/active_record/relation.rb, line 460
def scoping(all_queries: nil, &block)
  registry = klass.scope_registry
  if global_scope?(registry) && all_queries == false
    raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
  elsif already_in_scope?(registry)
    yield
  else
    _scoping(self, registry, all_queries, &block)
  end
end

size()

傳回記錄的大小。

# File activerecord/lib/active_record/relation.rb, line 274
def size
  if loaded?
    records.length
  else
    count(:all)
  end
end

to_a()

別名:to_ary

to_ary()

將關聯物件轉換為 Array

別名:to_a
# File activerecord/lib/active_record/relation.rb, line 258
def to_ary
  records.dup
end

to_sql()

傳回關聯的 SQL 陳述式。

User.where(name: 'Oscar').to_sql
# SELECT "users".* FROM "users"  WHERE "users"."name" = 'Oscar'
# File activerecord/lib/active_record/relation.rb, line 771
def to_sql
  @to_sql ||= if eager_loading?
    apply_join_dependency do |relation, join_dependency|
      relation = join_dependency.apply_column_aliases(relation)
      relation.to_sql
    end
  else
    conn = klass.connection
    conn.unprepared_statement { conn.to_sql(arel) }
  end
end

touch_all(*names, time: nil)

觸發目前關聯中的所有記錄,將 updated_at/updated_on 屬性設定為目前時間或指定的時間。它不會實例化所涉及的模型,也不會觸發 Active Record 回呼或驗證。此方法可以傳遞屬性名稱和一個時間參數(選用)。如果傳遞屬性名稱,它們會與 updated_at/updated_on 屬性一起更新。如果沒有傳遞時間參數,預設會使用目前時間。

範例

# Touch all records
Person.all.touch_all
# => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"

# Touch multiple records with a custom attribute
Person.all.touch_all(:created_at)
# => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"

# Touch multiple records with a specified time
Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
# => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"

# Touch records with scope
Person.where(name: 'David').touch_all
# => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
# File activerecord/lib/active_record/relation.rb, line 604
def touch_all(*names, time: nil)
  update_all klass.touch_attributes_with_time(*names, time: time)
end

update_all(updates)

使用提供的詳細資料更新目前關係中的所有記錄。此方法會建構單一 SQL UPDATE 陳述式,並直接將其傳送至資料庫。它不會實例化相關模型,也不會觸發 Active Record 回呼或驗證。不過,傳遞至 update_all 的值仍會經過 Active Record 的正常類型轉換和序列化。傳回受影響的列數。

注意:由於 Active Record 回呼並未觸發,此方法不會自動更新 updated_at/updated_on 欄位。

參數

  • updates - 代表 SQL 陳述式 SET 部分的字串、陣列或雜湊。提供的任何字串都會進行類型轉換,除非您使用 Arel.sql。(不要將使用者提供的值傳遞至 Arel.sql。)

範例

# Update all customers with the given attributes
Customer.update_all wants_email: true

# Update all books with 'Rails' in their title
Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')

# Update all books that match conditions, but limit it to 5 ordered by date
Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')

# Update all invoices and set the number column to its id value.
Invoice.update_all('number = id')

# Update all books with 'Rails' in their title
Book.where('title LIKE ?', '%Rails%').update_all(title: Arel.sql("title + ' - volume 1'"))
# File activerecord/lib/active_record/relation.rb, line 507
def update_all(updates)
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?

  return 0 if @none

  if updates.is_a?(Hash)
    if klass.locking_enabled? &&
        !updates.key?(klass.locking_column) &&
        !updates.key?(klass.locking_column.to_sym)
      attr = table[klass.locking_column]
      updates[attr.name] = _increment_attribute(attr)
    end
    values = _substitute_values(updates)
  else
    values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
  end

  arel = eager_loading? ? apply_join_dependency.arel : build_arel
  arel.source.left = table

  group_values_arel_columns = arel_columns(group_values.uniq)
  having_clause_ast = having_clause.ast unless having_clause.empty?
  stmt = arel.compile_update(values, table[primary_key], having_clause_ast, group_values_arel_columns)
  klass.connection.update(stmt, "#{klass} Update All").tap { reset }
end

update_counters(counters)

更新目前關係中記錄的計數器。

參數

  • counter - 雜湊,其中包含要更新的欄位名稱(做為金鑰),以及要更新的金額(做為值)。

  • :touch 選項 - 更新時觸發時間戳記欄位。

  • 如果傳遞屬性名稱,則會與 update_at/on 屬性一起更新。

範例

# For Posts by a given author increment the comment_count by 1.
Post.where(author_id: author.id).update_counters(comment_count: 1)
# File activerecord/lib/active_record/relation.rb, line 561
def update_counters(counters)
  touch = counters.delete(:touch)

  updates = {}
  counters.each do |counter_name, value|
    attr = table[counter_name]
    updates[attr.name] = _increment_attribute(attr, value)
  end

  if touch
    names = touch if touch != true
    names = Array.wrap(names)
    options = names.extract_options!
    touch_updates = klass.touch_attributes_with_time(*names, **options)
    updates.merge!(touch_updates) unless touch_updates.empty?
  end

  update_all updates
end

values()

# File activerecord/lib/active_record/relation.rb, line 838
def values
  @values.dup
end

實例保護方法

load_records(records)

# File activerecord/lib/active_record/relation.rb, line 887
def load_records(records)
  @records = records.freeze
  @loaded = true
end