- A
- C
- D
- E
- F
- G
- H
- I
- J
- L
- N
- O
- P
- R
- S
- U
- W
常數
FROZEN_EMPTY_ARRAY | = | [].freeze |
FROZEN_EMPTY_HASH | = | {}.freeze |
VALID_UNSCOPING_VALUES | = | Set.new([:where, :select, :group, :order, :lock, :limit, :offset, :joins, :left_outer_joins, :annotate, :includes, :eager_load, :preload, :from, :readonly, :having, :optimizer_hints, :with]) |
實例公開方法
and(other) 連結
返回一個新的關聯,它是此關聯與作為參數傳遞的關聯的邏輯交集。
兩個關聯必須在結構上相容:它們必須作用域於相同的模型,並且它們只能通過 where
(如果沒有定義 group
)或 having
(如果存在 group
)有所不同。
Post.where(id: [1, 2]).and(Post.where(id: [2, 3]))
# SELECT `posts`.* FROM `posts` WHERE `posts`.`id` IN (1, 2) AND `posts`.`id` IN (2, 3)
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1135 def and(other) if other.is_a?(Relation) spawn.and!(other) else raise ArgumentError, "You have passed #{other.class.name} object to #and. Pass an ActiveRecord::Relation object instead." end end
annotate(*args) 連結
將 SQL 註釋添加到由此關聯生成的查詢中。例如
User.annotate("selecting user names").select(:name)
# SELECT "users"."name" FROM "users" /* selecting user names */
User.annotate("selecting", "user", "names").select(:name)
# SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
SQL 塊註釋分隔符「/*」和「*/」將自動添加。
會執行一些跳脫,但不應該使用不受信任的使用者輸入。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1529 def annotate(*args) check_if_method_has_arguments!(__callee__, args) spawn.annotate!(*args) end
create_with(value) 連結
設定從關聯物件建立新記錄時要使用的屬性。
users = User.where(name: 'Oscar')
users.new.name # => 'Oscar'
users = users.create_with(name: 'DHH')
users.new.name # => 'DHH'
您可以傳遞 nil
給 create_with
以重置屬性
users = users.create_with(nil)
users.new.name # => 'Oscar'
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1346 def create_with(value) spawn.create_with!(value) end
distinct(value = true) 連結
指定記錄是否應唯一。例如
User.select(:name)
# Might return two records with the same name
User.select(:name).distinct
# Returns 1 record per distinct name
User.select(:name).distinct.distinct(false)
# You can also remove the uniqueness
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1410 def distinct(value = true) spawn.distinct!(value) end
eager_load(*args) 連結
使用 `LEFT OUTER JOIN` 指定要立即載入的關聯 `args`。執行單個查詢,連接所有指定的關聯。例如
users = User.eager_load(:address).limit(5)
users.each do |user|
user.address.city
end
# SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ... FROM "users"
# LEFT OUTER JOIN "addresses" ON "addresses"."id" = "users"."address_id"
# LIMIT 5
所有地址都使用單個連接查詢載入,而不是使用 5 個單獨的查詢載入 5 個地址。
可以使用雜湊和陣列載入多個和巢狀關聯,類似於 `includes`
User.eager_load(:address, friends: [:address, :followers])
# SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ... FROM "users"
# LEFT OUTER JOIN "addresses" ON "addresses"."id" = "users"."address_id"
# LEFT OUTER JOIN "friends" ON "friends"."user_id" = "users"."id"
# ...
注意:在連接中載入關聯可能會導致許多包含冗餘數據的行,並且在大規模情況下效能不佳。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 290 def eager_load(*args) check_if_method_has_arguments!(__callee__, args) spawn.eager_load!(*args) end
excluding(*records) 連結
從產生的關聯中排除指定的記錄(或記錄集合)。例如
Post.excluding(post)
# SELECT "posts".* FROM "posts" WHERE "posts"."id" != 1
Post.excluding(post_one, post_two)
# SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (1, 2)
Post.excluding(Post.drafts)
# SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (3, 4, 5)
這也可以在關聯上調用。與上述範例一樣,可以指定單個記錄或其集合
post = Post.find(1)
comment = Comment.find(2)
post.comments.excluding(comment)
# SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 AND "comments"."id" != 2
這是 `where.not(id: post.id)` 和 `where.not(id: [post_one.id, post_two.id])` 的簡寫。
如果未指定任何記錄,或者集合中的任何記錄(如果傳入集合)不是關聯所作用域的相同模型的實例,則會引發 `ArgumentError`。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1574 def excluding(*records) relations = records.extract! { |element| element.is_a?(Relation) } records.flatten!(1) records.compact! unless records.all?(model) && relations.all? { |relation| relation.model == model } raise ArgumentError, "You must only pass a single or collection of #{model.name} objects to ##{__callee__}." end spawn.excluding!(records + relations.flat_map(&:ids)) end
extending(*modules, &block) 連結
用於使用其他方法擴展作用域,可以通過模組或通過提供的區塊。
返回的物件是一個關聯,可以進一步擴展。
使用模組
module Pagination
def page(number)
# pagination code goes here
end
end
scope = Model.all.extending(Pagination)
scope.page(params[:page])
您也可以傳遞模組列表
scope = Model.all.extending(Pagination, SomethingElse)
使用區塊
scope = Model.all.extending do
def page(number)
# pagination code goes here
end
end
scope.page(params[:page])
您也可以使用區塊和模組列表
scope = Model.all.extending(Pagination) do
def per_page(number)
# pagination code goes here
end
end
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1456 def extending(*modules, &block) if modules.any? || block spawn.extending!(*modules, &block) else self end end
extract_associated(association) 連結
從關聯中提取名為 `association` 的關聯。首先預載入命名的關聯,然後從關聯中收集單個關聯記錄。像這樣
account.memberships.extract_associated(:user)
# => Returns collection of User records
這是以下內容的簡寫
account.memberships.preload(:user).collect(&:user)
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 341 def extract_associated(association) preload(association).collect(&association) end
from(value, subquery_name = nil) 連結
指定將從中擷取記錄的表格。例如
Topic.select('title').from('posts')
# SELECT title FROM posts
可以接受其他關聯物件。例如
Topic.select('title').from(Topic.approved)
# SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
傳遞第二個參數(字串或符號)會為 SQL from 子句建立別名。否則,將使用別名“subquery”
Topic.select('a.title').from(Topic.approved, :a)
# SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
它不會向 SQL from 子句添加多個參數。使用最後一個鏈接的 `from`
Topic.select('title').from(Topic.approved).from(Topic.inactive)
# SELECT title FROM (SELECT topics.* FROM topics WHERE topics.active = 'f') subquery
對於 SQL from 子句的多個參數,您可以傳遞一個字串,其中包含 SQL from 列表中的確切元素
color = "red"
Color
.from("colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)")
.where("colorvalue->>'color' = ?", color)
.select("c.*").to_a
# SELECT c.*
# FROM colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)
# WHERE (colorvalue->>'color' = 'red')
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1391 def from(value, subquery_name = nil) spawn.from!(value, subquery_name) end
group(*args) 連結
允許指定群組屬性
User.group(:name)
# SELECT "users".* FROM "users" GROUP BY name
根據 `group` 屬性返回具有不同記錄的陣列
User.select([:id, :name])
# => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">]
User.group(:name)
# => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
User.group('name AS grouped_name, age')
# => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
也支援傳入要分組的屬性陣列。
User.select([:id, :first_name]).group(:id, :first_name).first(3)
# => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 573 def group(*args) check_if_method_has_arguments!(__callee__, args) spawn.group!(*args) end
having(opts, *rest) 連結
允許指定 HAVING 子句。請注意,您不能在不指定 GROUP 子句的情況下使用 HAVING。
Order.having('SUM(price) > 30').group('user_id')
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1197 def having(opts, *rest) opts.blank? ? self : spawn.having!(opts, *rest) end
in_order_of(column, values, filter: true) 連結
基於給定的 `column` 應用 `ORDER BY` 子句,並由一組特定的 `values` 排序和過濾。
User.in_order_of(:id, [1, 5, 3])
# SELECT "users".* FROM "users"
# WHERE "users"."id" IN (1, 5, 3)
# ORDER BY CASE
# WHEN "users"."id" = 1 THEN 1
# WHEN "users"."id" = 5 THEN 2
# WHEN "users"."id" = 3 THEN 3
# END ASC
`column` 可以指向一個枚舉欄位;生成的實際查詢可能會因資料庫適配器和欄位定義而異。
class Conversation < ActiveRecord::Base
enum :status, [ :active, :archived ]
end
Conversation.in_order_of(:status, [:archived, :active])
# SELECT "conversations".* FROM "conversations"
# WHERE "conversations"."status" IN (1, 0)
# ORDER BY CASE
# WHEN "conversations"."status" = 1 THEN 1
# WHEN "conversations"."status" = 0 THEN 2
# END ASC
`values` 也可以包含 `nil`。
Conversation.in_order_of(:status, [nil, :archived, :active])
# SELECT "conversations".* FROM "conversations"
# WHERE ("conversations"."status" IN (1, 0) OR "conversations"."status" IS NULL)
# ORDER BY CASE
# WHEN "conversations"."status" IS NULL THEN 1
# WHEN "conversations"."status" = 1 THEN 2
# WHEN "conversations"."status" = 0 THEN 3
# END ASC
`filter` 可以設定為 `false` 以包含所有結果,而不是僅包含 `values` 中指定的結果。
Conversation.in_order_of(:status, [:archived, :active], filter: false)
# SELECT "conversations".* FROM "conversations"
# ORDER BY CASE
# WHEN "conversations"."status" = 1 THEN 1
# WHEN "conversations"."status" = 0 THEN 2
# ELSE 3
# END ASC
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 717 def in_order_of(column, values, filter: true) model.disallow_raw_sql!([column], permit: model.adapter_class.column_name_with_order_matcher) return spawn.none! if values.empty? references = column_references([column]) self.references_values |= references unless references.empty? values = values.map { |value| model.type_caster.type_cast_for_database(column, value) } arel_column = column.is_a?(Arel::Nodes::SqlLiteral) ? column : order_column(column.to_s) scope = spawn.order!(build_case_for_value_position(arel_column, values, filter: filter)) if filter where_clause = if values.include?(nil) arel_column.in(values.compact).or(arel_column.eq(nil)) else arel_column.in(values) end scope = scope.where!(where_clause) end scope end
includes(*args) 連結
指定要立即載入的關聯 `args` 以防止 N + 1 個查詢。除非條件需要連接,否則將為每個關聯執行單獨的查詢。
例如
users = User.includes(:address).limit(5)
users.each do |user|
user.address.city
end
# SELECT "users".* FROM "users" LIMIT 5
# SELECT "addresses".* FROM "addresses" WHERE "addresses"."id" IN (1,2,3,4,5)
所有地址都使用單個查詢載入,而不是使用 5 個單獨的查詢載入 5 個地址。
與簡單連接相比,在單獨的查詢中載入關聯通常會提高效能,因為連接可能會導致許多包含冗餘數據的行,並且在大規模情況下效能不佳。
您也可以指定多個關聯。每個關聯都將導致額外的查詢
User.includes(:address, :friends).to_a
# SELECT "users".* FROM "users"
# SELECT "addresses".* FROM "addresses" WHERE "addresses"."id" IN (1,2,3,4,5)
# SELECT "friends".* FROM "friends" WHERE "friends"."user_id" IN (1,2,3,4,5)
可以使用雜湊載入巢狀關聯
User.includes(:address, friends: [:address, :followers])
條件
如果要向包含的模型添加字串條件,則必須明確引用它們。例如
User.includes(:posts).where('posts.name = ?', 'example').to_a
會拋出錯誤,但這將起作用
User.includes(:posts).where('posts.name = ?', 'example').references(:posts).to_a
# SELECT "users"."id" AS t0_r0, ... FROM "users"
# LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
# WHERE "posts"."name" = ? [["name", "example"]]
由於 `LEFT OUTER JOIN` 已經包含文章,因此不再執行對文章的第二次查詢。
請注意,`includes` 使用關聯名稱,而 `references` 需要實際的表格名稱。
如果您通過 `Hash` 傳遞條件,則無需顯式調用 `references`,因為 `where` 會為您引用表格。例如,這將正常工作
User.includes(:posts).where(posts: { name: 'example' })
注意:條件會影響關聯的雙方。例如,上述程式碼將僅返回名稱為“example”的文章的使用者,*並且僅包含名稱為“example”的文章*,即使匹配的使用者還有其他文章也是如此。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 250 def includes(*args) check_if_method_has_arguments!(__callee__, args) spawn.includes!(*args) end
invert_where() 連結
允許您反轉整個 where 子句,而不需手動套用條件。
class User
scope :active, -> { where(accepted: true, locked: false) }
end
User.where(accepted: true)
# WHERE `accepted` = 1
User.where(accepted: true).invert_where
# WHERE `accepted` != 1
User.active
# WHERE `accepted` = 1 AND `locked` = 0
User.active.invert_where
# WHERE NOT (`accepted` = 1 AND `locked` = 0)
請小心,因為這會反轉在呼叫 invert_where
之前的**所有**條件。
class User
scope :active, -> { where(accepted: true, locked: false) }
scope :inactive, -> { active.invert_where } # Do not attempt it
end
# It also inverts `where(role: 'admin')` unexpectedly.
User.where(role: 'admin').inactive
# WHERE NOT (`role` = 'admin' AND `accepted` = 1 AND `locked` = 0)
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1101 def invert_where spawn.invert_where! end
joins(*args) 連結
對 args
執行 JOIN。給定的符號應與關聯的名稱相符。
User.joins(:posts)
# SELECT "users".*
# FROM "users"
# INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
多個 joins
User.joins(:posts, :account)
# SELECT "users".*
# FROM "users"
# INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
# INNER JOIN "accounts" ON "accounts"."id" = "users"."account_id"
巢狀 joins
User.joins(posts: [:comments])
# SELECT "users".*
# FROM "users"
# INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
# INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
您可以使用字串來自訂您的 joins
User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
# SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 868 def joins(*args) check_if_method_has_arguments!(__callee__, args) spawn.joins!(*args) end
left_outer_joins(*args) 連結
對 args
執行 LEFT OUTER JOIN。
User.left_outer_joins(:posts)
# SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 883 def left_outer_joins(*args) check_if_method_has_arguments!(__callee__, args) spawn.left_outer_joins!(*args) end
limit(value) 連結
指定要擷取的記錄數限制。
User.limit(10) # generated SQL has 'LIMIT 10'
User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1211 def limit(value) spawn.limit!(value) end
lock(locks = true) 連結
指定鎖定設定(預設為 true
)。有關鎖定的詳細資訊,請參閱 ActiveRecord::Locking
。
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1238 def lock(locks = true) spawn.lock!(locks) end
none() 連結
傳回一個沒有記錄的可串接關聯。
傳回的關聯實作了 Null Object
模式。它是一個具有已定義 null 行為的物件,並且始終傳回一個空的記錄陣列,而無需查詢資料庫。
任何串接到傳回關聯的後續條件都將繼續產生空的關聯,並且不會對資料庫發出任何查詢。
用於方法或範圍可能傳回零個記錄,但結果需要可串接的情況。
例如
@posts = current_user.visible_posts.where(name: params[:name])
# the visible_posts method is expected to return a chainable Relation
def visible_posts
case role
when 'Country Manager'
Post.where(country: country)
when 'Reviewer'
Post.published
when 'Bad User'
Post.none # It can't be chained if [] is returned.
end
end
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1281 def none spawn.none! end
offset(value) 連結
指定在傳回資料列之前要跳過的資料列數。
User.offset(10) # generated SQL has "OFFSET 10"
應與 order 一起使用。
User.offset(10).order("name ASC")
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1227 def offset(value) spawn.offset!(value) end
optimizer_hints(*args) 連結
指定要在 SELECT 語句中使用的最佳化器提示。
範例 (適用於 MySQL)
Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
# SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
範例 (適用於具有 pg_hint_plan 的 PostgreSQL)
Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
# SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1485 def optimizer_hints(*args) check_if_method_has_arguments!(__callee__, args) spawn.optimizer_hints!(*args) end
or(other) 連結
傳回一個新的關聯,它是此關聯與作為參數傳遞的關聯的邏輯聯集。
兩個關聯必須在結構上相容:它們必須作用域於相同的模型,並且它們只能通過 where
(如果沒有定義 group
)或 having
(如果存在 group
)有所不同。
Post.where("id = 1").or(Post.where("author_id = 3"))
# SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1167 def or(other) if other.is_a?(Relation) if @none other.spawn else spawn.or!(other) end else raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead." end end
order(*args) 連結
將 ORDER BY
子句套用至查詢。
order
接受以下幾種格式的參數。
符號
符號代表您要排序結果的欄位名稱。
User.order(:name)
# SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
預設情況下,順序是升序。如果您想要降序,您可以將欄位名稱符號映射到 :desc
。
User.order(email: :desc)
# SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
可以通過這種方式傳遞多個欄位,它們將按照指定的順序套用。
User.order(:name, email: :desc)
# SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
字串
字串直接傳遞到資料庫,允許您指定簡單的 SQL 運算式。
這可能是 SQL 注入的來源,因此只允許由純欄位名稱和簡單的 function(column_name)
運算式組成的字串,並可選擇使用 ASC
/DESC
修飾符。
User.order('name')
# SELECT "users".* FROM "users" ORDER BY name
User.order('name DESC')
# SELECT "users".* FROM "users" ORDER BY name DESC
User.order('name DESC, email')
# SELECT "users".* FROM "users" ORDER BY name DESC, email
Arel
如果您需要傳遞您已驗證對資料庫安全的複雜運算式,您可以使用 Arel
。
User.order(Arel.sql('end_date - start_date'))
# SELECT "users".* FROM "users" ORDER BY end_date - start_date
這種方式支援自定義查詢語法,例如 PostgreSQL 的 JSON 欄位。
User.order(Arel.sql("payload->>'kind'"))
# SELECT "users".* FROM "users" ORDER BY payload->>'kind'
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 656 def order(*args) check_if_method_has_arguments!(__callee__, args) do sanitize_order_arguments(args) end spawn.order!(*args) end
preload(*args) 連結
指定要使用單獨查詢積極載入的關聯 args
。每個關聯都會執行一個單獨的查詢。
users = User.preload(:address).limit(5)
users.each do |user|
user.address.city
end
# SELECT "users".* FROM "users" LIMIT 5
# SELECT "addresses".* FROM "addresses" WHERE "addresses"."id" IN (1,2,3,4,5)
所有地址都使用單獨的查詢載入,而不是使用 5 個單獨的查詢載入 5 個地址。
可以使用雜湊和陣列載入多個和巢狀關聯,類似於 `includes`
User.preload(:address, friends: [:address, :followers])
# SELECT "users".* FROM "users"
# SELECT "addresses".* FROM "addresses" WHERE "addresses"."id" IN (1,2,3,4,5)
# SELECT "friends".* FROM "friends" WHERE "friends"."user_id" IN (1,2,3,4,5)
# SELECT ...
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 322 def preload(*args) check_if_method_has_arguments!(__callee__, args) spawn.preload!(*args) end
readonly(value = true) 連結
將關聯標記為唯讀。嘗試更新記錄將導致錯誤。
users = User.readonly
users.first.save
=> ActiveRecord::ReadOnlyRecord: User is marked as readonly
要使唯讀關聯可寫入,請傳遞 false
。
users.readonly(false)
users.first.save
=> true
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1309 def readonly(value = true) spawn.readonly!(value) end
references(*table_names) 連結
用於指示給定的 table_names
被 SQL 字串所參考,因此應該在任何查詢中使用 +JOIN+,而不是單獨載入。此方法僅適用於 includes
。有關更多詳細資訊,請參閱 includes
。
User.includes(:posts).where("posts.name = 'foo'")
# Doesn't JOIN the posts table, resulting in an error.
User.includes(:posts).where("posts.name = 'foo'").references(:posts)
# Query now knows the string references posts, so adds a JOIN
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 355 def references(*table_names) check_if_method_has_arguments!(__callee__, table_names) spawn.references!(*table_names) end
regroup(*args) 連結
允許您更改先前設定的 group 語句。
Post.group(:title, :body)
# SELECT `posts`.`*` FROM `posts` GROUP BY `posts`.`title`, `posts`.`body`
Post.group(:title, :body).regroup(:title)
# SELECT `posts`.`*` FROM `posts` GROUP BY `posts`.`title`
這是 unscope(:group).group(fields)
的簡寫。請注意,我們正在取消整個 group 語句的範圍。
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 593 def regroup(*args) check_if_method_has_arguments!(__callee__, args) spawn.regroup!(*args) end
reorder(*args) 連結
將關聯上定義的任何現有順序替換為指定的順序。
User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
後續在同一個關聯上呼叫 order 將會被附加。例如
User.order('email DESC').reorder('id ASC').order('name ASC')
會產生一個帶有 ORDER BY id ASC, name ASC
的查詢。
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 752 def reorder(*args) check_if_method_has_arguments!(__callee__, args) do sanitize_order_arguments(args) end spawn.reorder!(*args) end
reselect(*args) 連結
允許您更改先前設定的 select 語句。
Post.select(:title, :body)
# SELECT `posts`.`title`, `posts`.`body` FROM `posts`
Post.select(:title, :body).reselect(:created_at)
# SELECT `posts`.`created_at` FROM `posts`
這是 unscope(:select).select(fields)
的簡寫。請注意,我們正在取消整個 select 語句的範圍。
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 541 def reselect(*args) check_if_method_has_arguments!(__callee__, args) args = process_select_args(args) spawn.reselect!(*args) end
reverse_order() 連結
反轉關聯上現有的 order 子句。
User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1498 def reverse_order spawn.reverse_order! end
rewhere(conditions) 連結
允許您更改先前為給定屬性設定的 where 條件,而不是附加到該條件。
Post.where(trashed: true).where(trashed: false)
# WHERE `trashed` = 1 AND `trashed` = 0
Post.where(trashed: true).rewhere(trashed: false)
# WHERE `trashed` = 0
Post.where(active: true).where(trashed: true).rewhere(trashed: false)
# WHERE `active` = 1 AND `trashed` = 0
這是 unscope(where: conditions.keys).where(conditions)
的簡寫。請注意,與 reorder 不同,我們只取消命名條件的範圍,而不是整個 where 語句。
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1061 def rewhere(conditions) return unscope(:where) if conditions.nil? scope = spawn where_clause = scope.build_where_clause(conditions) scope.unscope!(where: where_clause.extract_attributes) scope.where_clause += where_clause scope end
select(*fields) 連結
以兩種獨特的方式運作。
第一種:接受一個區塊,因此它可以像 Array#select
一樣使用。
Model.all.select { |m| m.field == value }
這將從資料庫中建立一個物件陣列,將其轉換為陣列,並使用 Array#select
對其進行迭代。
第二種:修改查詢的 SELECT 語句,以便只擷取某些欄位
Model.select(:field)
# => [#<Model id: nil, field: "value">]
儘管在上面的範例中看起來此方法傳回一個陣列,但它實際上傳回一個關聯物件,並且可以附加其他查詢方法,例如 ActiveRecord::QueryMethods
中的其他方法。
方法的參數也可以是欄位陣列。
Model.select(:field, :other_field, :and_one_more)
# => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
參數也可以是欄位和別名的雜湊。
Model.select(models: { field: :alias, other_field: :other_alias })
# => [#<Model id: nil, alias: "value", other_alias: "value">]
Model.select(models: [:field, :other_field])
# => [#<Model id: nil, field: "value", other_field: "value">]
您也可以使用一個或多個字串,它們將原封不動地用作 SELECT 欄位。
Model.select('field AS field_one', 'other_field AS field_two')
# => [#<Model id: nil, field_one: "value", field_two: "value">]
如果指定了別名,則可以從產生的物件中存取它
Model.select('field AS field_one').first.field_one
# => "value"
存取 select 未擷取其欄位的物件屬性(除了 id
)將會引發 ActiveModel::MissingAttributeError
Model.select(:field).first.other_field
# => ActiveModel::MissingAttributeError: missing attribute 'other_field' for Model
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 413 def select(*fields) if block_given? if fields.any? raise ArgumentError, "`select' with block doesn't take arguments." end return super() end check_if_method_has_arguments!(__callee__, fields, "Call `select' with at least one field.") fields = process_select_args(fields) spawn._select!(*fields) end
strict_loading(value = true) 連結
將傳回的關聯設定為 strict_loading
模式。如果記錄嘗試延遲載入關聯,這將會引發錯誤。
user = User.strict_loading.first
user.comments.to_a
=> ActiveRecord::StrictLoadingViolationError
原始碼:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1324 def strict_loading(value = true) spawn.strict_loading!(value) end
structurally_compatible?(other) 連結
檢查給定的關聯是否與此關聯在結構上相容,以確定是否可以在不引發錯誤的情況下使用 and
和 or
方法。結構相容的定義為:它們必須作用於相同的模型,並且它們只能在 where
(如果沒有定義 group
)或 having
(如果存在 group
)方面有所不同。
Post.where("id = 1").structurally_compatible?(Post.where("author_id = 3"))
# => true
Post.joins(:comments).structurally_compatible?(Post.where("id = 1"))
# => false
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1121 def structurally_compatible?(other) structurally_incompatible_values_for(other).empty? end
uniq!(name) 連結
去除重複的值。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1541 def uniq!(name) if values = @values[name] values.uniq! if values.is_a?(Array) && !values.empty? end self end
unscope(*args) 連結
移除關聯鏈上已定義的不需要的關聯。這在傳遞關聯鏈並希望修改關聯而不重新構建整個鏈時非常有用。
User.order('email DESC').unscope(:order) == User.all
方法參數是符號,對應於應取消作用域的方法的名稱。有效參數在 VALID_UNSCOPING_VALUES
中給出。該方法也可以使用多個參數調用。例如
User.order('email DESC').select('id').where(name: "John")
.unscope(:order, :select, :where) == User.all
還可以傳遞一個雜湊作為參數來取消特定 :where
值的作用域。這是通過傳遞具有一個鍵值對的雜湊來完成的。鍵應為 :where
,值應是要取消作用域的 where 值。例如
User.where(name: "John", active: true).unscope(where: :name)
== User.where(active: true)
此方法類似於 except,但與 except 不同的是,它在合併後仍然存在
User.order('email').merge(User.except(:order))
== User.order('email')
User.order('email').merge(User.unscope(:order))
== User.all
這表示它可以在關聯定義中使用
has_many :comments, -> { unscope(where: :trashed) }
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 806 def unscope(*args) check_if_method_has_arguments!(__callee__, args) spawn.unscope!(*args) end
where(*args) 連結
返回一個新的關聯,它是根據參數中的條件過濾當前關聯的結果。
where
接受幾種格式的條件。在以下示例中,給出了生成的 SQL 作為說明;實際生成的查詢可能會因資料庫適配器而異。
字串
不帶額外參數的單個字串作為 SQL 片段傳遞給查詢建構函式,並用於查詢的 where 子句中。
Client.where("orders_count = '2'")
# SELECT * from clients where orders_count = '2';
請注意,如果操作不當,則根據使用者輸入構建自己的字串可能會使您的應用程式遭受注入攻擊。作為替代方案,建議使用以下方法之一。
陣列
如果傳遞一個陣列,則陣列的第一個元素被視為模板,其餘元素被插入到模板中以生成條件。Active Record 負責構建查詢以避免注入攻擊,並將在需要時從 ruby 類型轉換為資料庫類型。元素按其出現的順序插入到字串中。
User.where(["name = ? and email = ?", "Joe", "joe@example.com"])
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
或者,您可以在模板中使用命名佔位符,並傳遞一個雜湊作為陣列的第二個元素。模板中的名稱將替換為雜湊中相應的值。
User.where(["name = :name and email = :email", { name: "Joe", email: "joe@example.com" }])
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
這可以使複雜查詢中的程式碼更具可讀性。
最後,您可以在模板中使用 sprintf 樣式的 % 轉義符。這與之前的方法略有不同;您有責任確保模板中的值被正確引用。這些值被傳遞給連接器進行引用,但呼叫者有責任確保它們在生成的 SQL 中用引號括起來。引用後,使用與 Ruby 核心方法 Kernel::sprintf
相同的轉義符插入值。
User.where(["name = '%s' and email = '%s'", "Joe", "joe@example.com"])
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
如果使用多個參數調用 where
,則將它們視為如同它們作為單個陣列的元素傳遞一樣。
User.where("name = :name and email = :email", { name: "Joe", email: "joe@example.com" })
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
使用字串指定條件時,您可以使用資料庫提供的任何運算符。雖然這提供了最大的靈活性,但您也可能無意中引入了對底層資料庫的依賴。如果您的程式碼旨在供一般使用,請使用多個資料庫後端進行測試。
雜湊
where
也接受雜湊條件,其中鍵是欄位,值是要搜尋的值。
欄位可以是符號或字串。值可以是單個值、陣列或範圍。
User.where(name: "Joe", email: "joe@example.com")
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com'
User.where(name: ["Alice", "Bob"])
# SELECT * FROM users WHERE name IN ('Alice', 'Bob')
User.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
# SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')
在 belongs_to 關聯的情況下,如果使用 ActiveRecord
物件作為值,則可以使用關聯鍵來指定模型。
author = Author.find(1)
# The following queries will be equivalent:
Post.where(author: author)
Post.where(author_id: author)
這也適用於多型 belongs_to 關聯
treasure = Treasure.create(name: 'gold coins')
treasure.price_estimates << PriceEstimate.create(price: 125)
# The following queries will be equivalent:
PriceEstimate.where(estimate_of: treasure)
PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)
Hash
條件也可以用類似元組的語法指定。Hash
鍵可以是帶有元組陣列作為值的欄位陣列。
Article.where([:author_id, :id] => [[15, 1], [15, 2]])
# SELECT * FROM articles WHERE author_id = 15 AND id = 1 OR author_id = 15 AND id = 2
聯結
如果關聯是聯結的結果,您可以建立一個使用聯結中任何表格的條件。對於字串和陣列條件,請在條件中使用表格名稱。
User.joins(:posts).where("posts.created_at < ?", Time.now)
對於雜湊條件,您可以在鍵中使用表格名稱,或使用子雜湊。
User.joins(:posts).where("posts.published" => true)
User.joins(:posts).where(posts: { published: true })
無參數
如果沒有傳遞參數,where
會返回一個新的 WhereChain
實例,可以與 WhereChain#not
、WhereChain#missing
或 WhereChain#associated
串連。
與 WhereChain#not
串連
User.where.not(name: "Jon")
# SELECT * FROM users WHERE name != 'Jon'
Post.where.associated(:author)
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."id" IS NOT NULL
與 WhereChain#missing
串連
Post.where.missing(:author)
# SELECT "posts".* FROM "posts"
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."id" IS NULL
空白條件
如果條件是任何空白物件,則 where
不做任何操作並返回當前關聯。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1033 def where(*args) if args.empty? WhereChain.new(spawn) elsif args.length == 1 && args.first.blank? self else spawn.where!(*args) end end
with(*args) 連結
新增一個通用表表達式 (CTE),您可以在另一個 SELECT 陳述式中引用它。
注意:CTE 僅在 MySQL 8.0 及更高版本中受支援。您將無法在 MySQL 5.7 中使用 CTE。
Post.with(posts_with_tags: Post.where("tags_count > ?", 0))
# => ActiveRecord::Relation
# WITH posts_with_tags AS (
# SELECT * FROM posts WHERE (tags_count > 0)
# )
# SELECT * FROM posts
您也可以傳遞一個子查詢陣列,以便在 +UNION ALL+ 中聯結。
Post.with(posts_with_tags_or_comments: [Post.where("tags_count > ?", 0), Post.where("comments_count > ?", 0)])
# => ActiveRecord::Relation
# WITH posts_with_tags_or_comments AS (
# (SELECT * FROM posts WHERE (tags_count > 0))
# UNION ALL
# (SELECT * FROM posts WHERE (comments_count > 0))
# )
# SELECT * FROM posts
定義通用表表達式後,您可以使用自訂 FROM
值或 JOIN
來引用它。
Post.with(posts_with_tags: Post.where("tags_count > ?", 0)).from("posts_with_tags AS posts")
# => ActiveRecord::Relation
# WITH posts_with_tags AS (
# SELECT * FROM posts WHERE (tags_count > 0)
# )
# SELECT * FROM posts_with_tags AS posts
Post.with(posts_with_tags: Post.where("tags_count > ?", 0)).joins("JOIN posts_with_tags ON posts_with_tags.id = posts.id")
# => ActiveRecord::Relation
# WITH posts_with_tags AS (
# SELECT * FROM posts WHERE (tags_count > 0)
# )
# SELECT * FROM posts JOIN posts_with_tags ON posts_with_tags.id = posts.id
建議將查詢作為 ActiveRecord::Relation
傳遞。如果無法做到這一點,並且您已驗證它對資料庫是安全的,則可以使用 Arel
將其作為 SQL 字面量傳遞。
Post.with(popular_posts: Arel.sql("... complex sql to calculate posts popularity ..."))
應格外小心,以避免 SQL 注入漏洞。此方法不應與包含未清理輸入的不安全值一起使用。
要新增多個 CTE,只需傳遞多個鍵值對
Post.with(
posts_with_comments: Post.where("comments_count > ?", 0),
posts_with_tags: Post.where("tags_count > ?", 0)
)
或鏈接多個 .with
呼叫
Post
.with(posts_with_comments: Post.where("comments_count > ?", 0))
.with(posts_with_tags: Post.where("tags_count > ?", 0))
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 493 def with(*args) raise ArgumentError, "ActiveRecord::Relation#with does not accept a block" if block_given? check_if_method_has_arguments!(__callee__, args) spawn.with!(*args) end
with_recursive(*args) 連結
新增一個遞迴通用表表達式 (CTE),您可以在另一個 SELECT 陳述式中引用它。
Post.with_recursive(post_and_replies: [Post.where(id: 42), Post.joins('JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id')])
# => ActiveRecord::Relation
# WITH RECURSIVE post_and_replies AS (
# (SELECT * FROM posts WHERE id = 42)
# UNION ALL
# (SELECT * FROM posts JOIN posts_and_replies ON posts.in_reply_to_id = posts_and_replies.id)
# )
# SELECT * FROM posts
有關更多資訊,請參閱「#with」。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 518 def with_recursive(*args) check_if_method_has_arguments!(__callee__, args) spawn.with_recursive!(*args) end
實例保護方法
arel_columns(columns) 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1662 def arel_columns(columns) columns.flat_map do |field| case field when Symbol, String arel_column(field) when Proc field.call when Hash arel_columns_from_hash(field) else field end end end
async!() 連結
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/query_methods.rb, line 1656 def async! @async = true self end