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, :with_recursive] |
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(model, table: nil, predicate_builder: nil, values: {}) 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 77 def initialize(model, table: nil, predicate_builder: nil, values: {}) if table predicate_builder ||= model.predicate_builder.with(TableMetadata.new(model, table)) else table = model.arel_table predicate_builder ||= model.predicate_builder end @model = model @table = table @values = values @loaded = false @predicate_builder = predicate_builder @delegate_to_model = false @future_result = nil @records = nil @async = false @none = false end
實例公開方法
==(other) 連結
比較兩個關聯式是否相等。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1253 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
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 391 def any?(*args) return false if @none return super if args.present? || block_given? !empty? end
blank?() 連結
如果關聯式為空,則返回 true。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1274 def blank? records.blank? end
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)
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 438 def cache_key(timestamp_column = "updated_at") @cache_keys ||= {} @cache_keys[timestamp_column] ||= model.collection_cache_key(self, timestamp_column) end
cache_key_with_version() 連結
返回一個快取鍵以及版本。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 519 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%')
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 465 def cache_version(timestamp_column = :updated_at) if model.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, ...>
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 154 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! 相同。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 169 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(attributes, &block) 連結
嘗試在一個或多個欄位上具有唯一資料庫約束的表格中使用給定屬性創建記錄。如果具有這些唯一約束之一或多個約束的列已存在,則會捕獲此類插入通常會引發的異常,並使用 find_by! 找到具有這些屬性的現有記錄。
這類似於 find_or_create_by,但會先嘗試創建記錄。因此,它更適合於記錄很可能尚不存在的情況。
create_or_find_by 有一些缺點,
-
基礎表格必須使用唯一資料庫約束定義相關欄位。
-
唯一約束違規可能僅由給定屬性中的一個或至少少於全部屬性觸發。這意味著後續的 find_by! 可能找不到匹配的記錄,然後會引發 ActiveRecord::RecordNotFound 異常,而不是具有給定屬性的記錄。
-
雖然我們避免了 find_or_create_by 中 SELECT -> INSERT 之間的競爭條件,但我們實際上在 INSERT -> SELECT 之間還有另一個競爭條件,如果另一個客戶端在 delete 這兩個語句之間運行 DELETE,則可能會觸發該競爭條件。但對於大多數應用程式來說,這種情況發生的可能性要小得多。
-
它依賴異常處理來處理控制流程,這可能會稍微慢一些。
-
即使失敗,主鍵也可能在每次創建時自動遞增。如果基礎表格仍然停留在 int 類型的主鍵上,這會加速整數用盡的問題(注意:自 5.1+ 以來,所有 Rails 應用程式都默認使用 bigint,這不容易出現此問題)。
-
具有唯一資料庫約束的欄位不應定義唯一性驗證,否則 create 將因驗證錯誤而失敗,並且永遠不會調用 find_by。
如果所有給定屬性都被唯一約束覆蓋(除非觸發了 INSERT -> DELETE -> SELECT 競爭條件),此方法將返回一條記錄,但如果嘗試創建失敗是由於驗證錯誤而未持久化,您將獲得 create 在這種情況下返回的內容。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 273 def create_or_find_by(attributes, &block) with_connection do |connection| 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 end
create_or_find_by!(attributes, &block) 連結
類似於 create_or_find_by,但調用 create!,因此如果創建的記錄無效,則會引發異常。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 288 def create_or_find_by!(attributes, &block) with_connection do |connection| 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 end
delete(id_or_array) 連結
使用 SQL DELETE 語句刪除主鍵與 id 參數匹配的列,並返回已刪除的列數。Active Record 物件未實例化,因此物件的回調未執行,包括任何 :dependent 關聯選項。
您可以透過傳遞 id 的陣列一次刪除多列。
注意:儘管它通常比替代方法 destroy 快得多,但跳過回調可能會繞過應用程式中的業務邏輯,這些邏輯可確保參考完整性或執行其他重要工作。
範例
# Delete a single row
Todo.delete(1)
# Delete multiple rows
Todo.delete([2,3,4])
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1057 def delete(id_or_array) return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?) where(model.primary_key => id_or_array).delete_all 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
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1011 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 model.with_connection do |c| arel = eager_loading? ? apply_join_dependency.arel : build_arel(c) arel.source.left = table group_values_arel_columns = arel_columns(group_values.uniq) having_clause_ast = having_clause.ast unless having_clause.empty? key = if model.composite_primary_key? primary_key.map { |pk| table[pk] } else table[primary_key] end stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns) c.delete(stmt, "#{model} Delete All").tap { reset } end 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)
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1119 def delete_by(*args) where(*args).delete_all end
destroy(id) 連結
銷毀具有給定 ID 的物件(或多個物件)。物件會先被實例化,因此所有回調和過濾器都會在物件被刪除之前觸發。此方法效率低於 delete
,但允許執行清理方法和其他操作。
這實際上是先找到具有給定 ID 的物件(或多個物件),從屬性創建一個新的物件,然後在其上呼叫 destroy。
參數
-
id
- 這應該是待銷毀的 ID 或 ID 陣列。
範例
# Destroy a single object
Todo.destroy(1)
# Destroy multiple objects
todos = [1,2,3]
Todo.destroy(todos)
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1083 def destroy(id) multiple_ids = if model.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
destroy_all() 連結
通過實例化每個記錄並呼叫其 #destroy 方法來銷毀記錄。每個物件的回調都會被執行(包括 :dependent
關聯選項)。返回已銷毀的物件集合;每個物件都將被凍結,以反映不應進行任何更改(因為它們無法被保存)。
注意:一次刪除多個記錄時,實例化、回調執行和刪除每個記錄可能會很耗時。它會為每個記錄生成至少一個 SQL DELETE
查詢(或者可能更多,以強制執行您的回調)。如果您想快速刪除多行,而不考慮它們的關聯或回調,請改用 delete_all
。
範例
Person.where(age: 0..18).destroy_all
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 989 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)
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1106 def destroy_by(*args) where(*args).destroy_all end
eager_loading?() 連結
如果關聯需要預先載入,則返回 true。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1238 def eager_loading? @should_eager_load ||= eager_load_values.any? || includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?) end
empty?() 連結
如果沒有記錄,則返回 true。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 362 def empty? return true if @none if loaded? records.empty? else !exists? end end
encode_with(coder) 連結
將關聯物件序列化為 陣列
。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 348 def encode_with(coder) coder.represent_seq(nil, records) end
explain(*options) 連結
對此關聯觸發的查詢執行 EXPLAIN,並將結果以字串形式返回。該字串的格式模仿資料庫 shell 列印的格式。
User.all.explain
# EXPLAIN SELECT `users`.* FROM `users`
# ...
請注意,此方法實際上會執行查詢,因為當預先載入正在進行時,後續查詢需要某些查詢的結果。
要在 first
、pluck
和 count
創建的查詢上運行 EXPLAIN,請在 explain
上呼叫這些方法
User.all.explain.count
# EXPLAIN SELECT COUNT(*) FROM `users`
# ...
如果需要,可以傳遞欄位名稱
User.all.explain.maximum(:id)
# EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
# ...
請參閱 Active Record 查詢介面指南 中的更多詳細資訊。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 332 def explain(*options) ExplainProxy.new(self, options) end
find_or_create_by(attributes, &block) 連結
查找具有給定屬性的第一個記錄,如果找不到,則使用這些屬性創建一個記錄
# 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。因此,如果表格沒有相關的唯一約束,則可能會出現兩個或多個類似記錄的情況。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 231 def find_or_create_by(attributes, &block) find_by(attributes) || create_or_find_by(attributes, &block) end
find_or_create_by!(attributes, &block) 連結
類似於 find_or_create_by
,但呼叫 create!,因此如果創建的記錄無效,則會引發異常。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 238 def find_or_create_by!(attributes, &block) find_by(attributes) || create_or_find_by!(attributes, &block) end
find_or_initialize_by(attributes, &block) 連結
類似於 find_or_create_by
,但呼叫 new 而不是 create。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 302 def find_or_initialize_by(attributes, &block) find_by(attributes) || new(attributes, &block) end
initialize_copy(other) 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 97 def initialize_copy(other) @values = @values.dup reset end
insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil) 連結
使用單個 SQL INSERT 語句將單個記錄插入資料庫。它不會實例化任何模型,也不會觸發 Active Record 回調或驗證。但是,傳遞的值會經過 Active Record 的類型轉換和序列化。
請參閱 insert_all
的文件。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 644 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) 連結
使用單個 SQL INSERT 語句將單個記錄插入資料庫。它不會實例化任何模型,也不會觸發 Active Record 回調或驗證。但是,傳遞的值會經過 Active Record 的類型轉換和序列化。
請參閱 insert_all!
了解更多資訊。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 733 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) 連結
使用單個 SQL INSERT 語句將多個記錄插入資料庫。它不會實例化任何模型,也不會觸發 Active Record 回調或驗證。但是,傳遞的值會經過 Active Record 的類型轉換和序列化。
attributes
參數是一個雜湊的 陣列
。每個 雜湊
決定單行的屬性,並且必須具有相同的鍵。
表格上的每個唯一索引都將行視為唯一。任何重複的行都會被跳過。使用 :unique_by
覆蓋(見下文)。
返回一個 ActiveRecord::Result
,其內容基於 :returning
(見下文)。
選項
- :returning
-
(僅限 PostgreSQL、SQLite3 和 MariaDB)要為所有成功插入的記錄返回的屬性陣列,預設情況下是主鍵。傳遞
returning: %w[ id name ]
以同時返回 id 和 name,或傳遞returning: false
以完全省略底層的RETURNING
SQL 子句。如果您需要更多地控制返回值,您也可以傳遞一個 SQL 字串(例如,
returning: Arel.sql("id, name as new_name")
)。 - :unique_by
-
(僅限 PostgreSQL 和 SQLite)預設情況下,表格上的每個唯一索引都將行視為唯一。任何重複的行都會被跳過。
要根據一個唯一索引跳過行,請傳遞
:unique_by
。考慮一個 Book 模型,其中不允許重複的 ISBN,但如果任何一行具有現有的 ID,或者不符合其他唯一索引的唯一性,則會引發
ActiveRecord::RecordNotUnique
錯誤。唯一索引可以通過欄位或名稱來識別。
unique_by: :isbn unique_by: %i[ author_id name ] unique_by: :index_books_on_isbn
- :record_timestamps
-
預設情況下,時間戳記欄位的自動設定由模型的
record_timestamps
設定控制,與典型行為相符。要覆蓋此設定並強制以某種方式自動設定時間戳記欄位,請傳遞
:record_timestamps
參數。record_timestamps: true # Always set timestamps automatically record_timestamps: false # Never set timestamps automatically
因為 :unique_by
依靠資料庫的索引資訊,建議將其與 Active Record 的 schema_cache 搭配使用。
範例
# 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/relation.rb, line 723 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
insert_all!(attributes, returning: nil, record_timestamps: nil) 連結
使用單個 SQL INSERT 語句將多個記錄插入資料庫。它不會實例化任何模型,也不會觸發 Active Record 回調或驗證。但是,傳遞的值會經過 Active Record 的類型轉換和序列化。
attributes
參數是一個雜湊的 陣列
。每個 雜湊
決定單行的屬性,並且必須具有相同的鍵。
如果任何一行違反表格上的唯一索引,則會引發 ActiveRecord::RecordNotUnique
錯誤。在這種情況下,不會插入任何一行。
要跳過重複的行,請參閱 insert_all
。要取代它們,請參閱 upsert_all
。
返回一個 ActiveRecord::Result
,其內容基於 :returning
(見下文)。
選項
- :returning
-
(僅限 PostgreSQL、SQLite3 和 MariaDB)要為所有成功插入的記錄返回的屬性陣列,預設情況下是主鍵。傳遞
returning: %w[ id name ]
以同時返回 id 和 name,或傳遞returning: false
以完全省略底層的RETURNING
SQL 子句。如果您需要更多地控制返回值,您也可以傳遞一個 SQL 字串(例如,
returning: Arel.sql("id, name as new_name")
)。 - :record_timestamps
-
預設情況下,時間戳記欄位的自動設定由模型的
record_timestamps
設定控制,與典型行為相符。要覆蓋此設定並強制以某種方式自動設定時間戳記欄位,請傳遞
:record_timestamps
參數。record_timestamps: true # Always set timestamps automatically record_timestamps: false # Never set timestamps automatically
範例
# 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/relation.rb, line 790 def insert_all!(attributes, returning: nil, record_timestamps: nil) InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps) end
inspect() 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1290 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() 連結
同時標記為預載入的 JOIN。在這種情況下,我們應該直接 Eager Load 它們。請注意,這是一個簡化的實現,因為我們可能會有代表相同關聯的字串和符號,但它們不會以此方式匹配。此外,我們可能會有部分匹配的巢狀雜湊,例如 { a: :b } & { a: [:b, :c] }
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1248 def joined_includes_values includes_values & joins_values end
load(&block) 連結
如果記錄尚未從資料庫載入,則會導致記錄從資料庫載入。如果由於某種原因,您需要在實際使用記錄之前明確載入某些記錄,則可以使用此方法。傳回值是關聯本身,而不是記錄。
Post.where(published: true).load # => #<ActiveRecord::Relation>
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1179 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 才能真正地並行執行查詢。否則,預設會在前景執行它們。
如果查詢實際上是在背景執行的,Active Record 的日誌將會在日誌行的開頭加上 `ASYNC` 來顯示它。
ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1138 def load_async with_connection do |c| return load if !c.async_enabled? unless loaded? result = exec_main_query(async: !c.current_transaction.joinable?) if result.is_a?(Array) @records = result else @future_result = result end @loaded = true end end self end
many?() 連結
如果有多個記錄,則傳回 true。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 413 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
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 125 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
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 378 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
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 404 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) 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1264 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
readonly?() 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1278 def readonly? readonly_value end
reload() 連結
強制重新載入關聯。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1189 def reload reset load end
reset() 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1194 def reset @future_result&.cancel @future_result = nil @delegate_to_model = false @to_sql = @arel = @loaded = @should_eager_load = nil @offsets = @take = nil @cache_keys = nil @cache_versions = nil @records = nil self end
scheduled?() 連結
如果關聯已排程在背景執行緒池上,則傳回 true
。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1169 def scheduled? !!@future_result end
scope_for_create() 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1231 def scope_for_create hash = where_clause.to_h(model.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
,則範圍設定將套用至關聯的所有查詢,包括對實例的 update
和 delete
。一旦 all_queries
設定為 true,則在巢狀區塊中就無法將其設定為 false。
如果您想在執行區塊期間移除所有先前的範圍(包括 default_scope),請查看 unscoped。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 541 def scoping(all_queries: nil, &block) registry = model.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() 連結
傳回記錄的大小。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 353 def size if loaded? records.length else count(:all) end end
to_ary() 連結
將關聯物件轉換為 陣列
。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 337 def to_ary records.dup end
to_sql() 連結
傳回關聯的 SQL 陳述式。
User.where(name: 'Oscar').to_sql
# SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1210 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 model.with_connection do |conn| conn.unprepared_statement { conn.to_sql(arel) } end 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'"
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 969 def touch_all(*names, time: nil) update_all model.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'"))
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 588 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 model.locking_enabled? && !updates.key?(model.locking_column) && !updates.key?(model.locking_column.to_sym) attr = table[model.locking_column] updates[attr.name] = _increment_attribute(attr) end values = _substitute_values(updates) else values = Arel.sql(model.sanitize_sql_for_assignment(updates, table.name)) end model.with_connection do |c| arel = eager_loading? ? apply_join_dependency.arel : build_arel(c) arel.source.left = table group_values_arel_columns = arel_columns(group_values.uniq) having_clause_ast = having_clause.ast unless having_clause.empty? key = if model.composite_primary_key? primary_key.map { |pk| table[pk] } else table[primary_key] end stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns) c.update(stmt, "#{model} Update All").tap { reset } end 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)
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 926 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 = model.touch_attributes_with_time(*names, **options) updates.merge!(touch_updates) unless touch_updates.empty? end update_all updates end
upsert(attributes, **kwargs) 連結
在單個 SQL INSERT 陳述式中更新或插入(upsert)單個記錄到資料庫中。它不會實例化任何模型,也不會觸發 Active Record 回呼或驗證。儘管傳遞的值會經過 Active Record 的類型轉換和序列化。
請參閱 upsert_all
的文件。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 800 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) 連結
在單個 SQL INSERT 語句中更新或插入(upsert)多個記錄到資料庫。它不會實例化任何模型,也不會觸發 Active Record 回調或驗證。但是傳遞的值會經過 Active Record 的類型轉換和序列化。
attributes
參數是一個雜湊的 陣列
。每個 雜湊
決定單行的屬性,並且必須具有相同的鍵。
返回一個 ActiveRecord::Result
,其內容基於 :returning
(見下文)。
預設情況下,當發生衝突時,`upsert_all` 會更新所有可以更新的欄位。這些欄位包含除了主鍵、唯讀欄位以及可選的 `unique_by` 涵蓋的欄位之外的所有欄位。
選項
- :returning
-
(僅限 PostgreSQL、SQLite3 和 MariaDB)要為所有成功插入的記錄返回的屬性陣列,預設情況下是主鍵。傳遞
returning: %w[ id name ]
以同時返回 id 和 name,或傳遞returning: false
以完全省略底層的RETURNING
SQL 子句。如果您需要更多地控制返回值,您也可以傳遞一個 SQL 字串(例如,
returning: Arel.sql("id, name as new_name")
)。 - :unique_by
-
(僅限 PostgreSQL 和 SQLite)預設情況下,表格上的每個唯一索引都將行視為唯一。任何重複的行都會被跳過。
要根據一個唯一索引跳過行,請傳遞
:unique_by
。考慮一個 Book 模型,其中不允許重複的 ISBN,但如果任何一行具有現有的 ID,或者不符合其他唯一索引的唯一性,則會引發
ActiveRecord::RecordNotUnique
錯誤。唯一索引可以通過欄位或名稱來識別。
unique_by: :isbn unique_by: %i[ author_id name ] unique_by: :index_books_on_isbn
因為 :unique_by
依靠資料庫的索引資訊,建議將其與 Active Record 的 schema_cache 搭配使用。
- :on_duplicate
-
設定在發生衝突時將使用的 SQL 更新語句。
注意:如果您使用此選項,您必須自行提供所有要更新的欄位。
範例
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)") )
請參閱相關的 `:update_only` 選項。兩個選項不能同時使用。
- :update_only
-
提供在發生衝突時將更新的欄位名稱列表。如果未提供,`upsert_all` 將更新所有可更新的欄位。這些欄位包含除了主鍵、唯讀欄位以及可選的 `unique_by` 涵蓋的欄位之外的所有欄位。
範例
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 )
請參閱相關的 `:on_duplicate` 選項。兩個選項不能同時使用。
- :record_timestamps
-
預設情況下,時間戳記欄位的自動設定由模型的
record_timestamps
設定控制,與典型行為相符。要覆蓋此設定並強制以某種方式自動設定時間戳記欄位,請傳遞
:record_timestamps
參數。record_timestamps: true # Always set timestamps automatically record_timestamps: false # Never set timestamps automatically
範例
# 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/relation.rb, line 910 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
values() 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1282 def values @values.dup end
實例保護方法(Instance Protected methods)
load_records(records) 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation.rb, line 1331 def load_records(records) @records = records.freeze @loaded = true end