module ActiveRecord::QueryMethods
Константы
- FROZEN_EMPTY_ARRAY
- FROZEN_EMPTY_HASH
- VALID_UNSCOPING_VALUES
Открытые методы экземпляра
# File activerecord/lib/active_record/relation/query_methods.rb, line 818
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 Возвращает новое отношение, представляющее собой логическое пересечение этого отношения и переданного в качестве аргумента.
Два отношения должны быть структурно совместимы: они должны охватывать одну и ту же модель, и они должны отличаться только по 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)
# File activerecord/lib/active_record/relation/query_methods.rb, line 1192 def annotate(*args) check_if_method_has_arguments!(__callee__, args) spawn.annotate!(*args) end
Добавляет комментарий 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, «/*» и «*/», будут добавлены автоматически.
# File activerecord/lib/active_record/relation/query_methods.rb, line 1011 def create_with(value) spawn.create_with!(value) end
Устанавливает атрибуты, которые будут использоваться при создании новых записей из объекта отношения.
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'
# File activerecord/lib/active_record/relation/query_methods.rb, line 1075 def distinct(value = true) spawn.distinct!(value) end
Указывает, должны ли записи быть уникальными или нет. Например:
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
# File activerecord/lib/active_record/relation/query_methods.rb, line 194 def eager_load(*args) check_if_method_has_arguments!(__callee__, args) spawn.eager_load!(*args) end
Принудительно выполняет нетерпеливую загрузку, выполняя LEFT OUTER JOIN по args:
User.eager_load(:posts) # SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ... # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = # "users"."id"
# File activerecord/lib/active_record/relation/query_methods.rb, line 1234
def excluding(*records)
records.flatten!(1)
records.compact!
unless records.all?(klass)
raise ArgumentError, "You must only pass a single or collection of #{klass.name} objects to ##{__callee__}."
end
spawn.excluding!(records)
end Исключает указанную запись (или коллекцию записей) из результирующего отношения. Например:
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 = 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 будет выброшено, если не указаны записи или если какие-либо записи в коллекции (если передана коллекция) не являются экземплярами той же модели, которую охватывает отношение.
# File activerecord/lib/active_record/relation/query_methods.rb, line 1121
def extending(*modules, &block)
if modules.any? || block
spawn.extending!(*modules, &block)
else
self
end
end Используется для расширения области видимости дополнительными методами, либо через модуль, либо через предоставленный блок.
Возвращаемый объект является отношением, которое может быть дополнительно расширено.
Использование модуля
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
# File activerecord/lib/active_record/relation/query_methods.rb, line 227 def extract_associated(association) preload(association).collect(&association) end
Извлекает именованную association из отношения. Именованная ассоциация сначала предварительно загружается, затем отдельные записи ассоциации собираются из отношения. Таким образом:
account.memberships.extract_associated(:user) # => Returns collection of User records
Это сокращение для:
account.memberships.preload(:user).collect(&:user)
# File activerecord/lib/active_record/relation/query_methods.rb, line 1056 def from(value, subquery_name = nil) spawn.from!(value, subquery_name) end
Указывает таблицу, из которой будут извлекаться записи. Например:
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')
# File activerecord/lib/active_record/relation/query_methods.rb, line 350 def group(*args) check_if_method_has_arguments!(__callee__, args) spawn.group!(*args) end
Позволяет указать группирующий атрибут:
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">]
# File activerecord/lib/active_record/relation/query_methods.rb, line 876 def having(opts, *rest) opts.blank? ? self : spawn.having!(opts, *rest) end
Позволяет указать предложение HAVING. Обратите внимание, что вы не можете использовать HAVING, не указав также предложение GROUP.
Order.having('SUM(price) > 30').group('user_id')
# File activerecord/lib/active_record/relation/query_methods.rb, line 434
def in_order_of(column, values)
klass.disallow_raw_sql!([column], permit: connection.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| type_caster.type_cast_for_database(column, value) }
arel_column = column.is_a?(Symbol) ? order_column(column.to_s) : column
spawn
.order!(connection.field_ordered_value(arel_column, values))
.where!(arel_column.in(values))
end Позволяет указать порядок по определенному набору значений. В зависимости от вашего адаптера это будет использовать либо оператор CASE, либо встроенную функцию.
User.in_order_of(:id, [1, 5, 3])
# SELECT "users".* FROM "users"
# ORDER BY FIELD("users"."id", 1, 5, 3)
# WHERE "users"."id" IN (1, 5, 3)
# File activerecord/lib/active_record/relation/query_methods.rb, line 178 def includes(*args) check_if_method_has_arguments!(__callee__, args) spawn.includes!(*args) end
Укажите отношения, которые должны быть включены в результирующий набор. Например:
users = User.includes(:address) users.each do |user| user.address.city end
позволяет вам получить доступ к атрибуту address модели User без запуска дополнительного запроса. Это часто приводит к повышению производительности по сравнению с простым объединением.
Вы также можете указать несколько отношений, например:
users = User.includes(:address, :friends)
Загрузка вложенных отношений возможна с помощью Hash:
users = User.includes(:address, friends: [:address, :followers])
Условия
Если вы хотите добавить строковые условия к вашим включенным моделям, вам нужно будет явно ссылаться на них. Например:
User.includes(:posts).where('posts.name = ?', 'example')
Вызовет ошибку, но это будет работать:
User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
Обратите внимание, что includes работает с именами ассоциаций, а references требует фактического имени таблицы.
Если вы передаете условия через hash, вам не нужно явно вызывать references, так как where ссылается на таблицы за вас. Например, это будет работать корректно:
User.includes(:posts).where(posts: { name: 'example' })
# File activerecord/lib/active_record/relation/query_methods.rb, line 784 def invert_where spawn.invert_where! end
Позволяет инвертировать все предложение 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)
# File activerecord/lib/active_record/relation/query_methods.rb, line 573 def joins(*args) check_if_method_has_arguments!(__callee__, args) spawn.joins!(*args) end
Выполняет JOIN по args. Указанные символы должны соответствовать имени(ям) ассоциации(й).
User.joins(:posts) # SELECT "users".* # FROM "users" # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
Множественные соединения:
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"
Вложенные соединения:
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"
Вы можете использовать строки для настройки ваших соединений:
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
# File activerecord/lib/active_record/relation/query_methods.rb, line 588 def left_outer_joins(*args) check_if_method_has_arguments!(__callee__, args) spawn.left_outer_joins!(*args) end
Выполняет LEFT OUTER JOINs на args:
User.left_outer_joins(:posts) => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
# File activerecord/lib/active_record/relation/query_methods.rb, line 890 def limit(value) spawn.limit!(value) end
Задает ограничение на количество записей для извлечения.
User.limit(10) # generated SQL has 'LIMIT 10' User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
# File activerecord/lib/active_record/relation/query_methods.rb, line 917 def lock(locks = true) spawn.lock!(locks) end
Задает параметры блокировки (по умолчанию true). Для получения дополнительной информации о блокировке см. ActiveRecord::Locking.
# File activerecord/lib/active_record/relation/query_methods.rb, line 960 def none spawn.none! end
Возвращает цепочечное отношение с нулевым количеством записей.
Возвращаемое отношение реализует шаблон 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
# File activerecord/lib/active_record/relation/query_methods.rb, line 906 def offset(value) spawn.offset!(value) end
Указывает количество строк, которые нужно пропустить перед возвратом строк.
User.offset(10) # generated SQL has "OFFSET 10"
Должно использоваться с order.
User.offset(10).order("name ASC")
# File activerecord/lib/active_record/relation/query_methods.rb, line 1150 def optimizer_hints(*args) check_if_method_has_arguments!(__callee__, args) spawn.optimizer_hints!(*args) end
Укажите подсказки оптимизатора, которые будут использоваться в операторе 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`
Пример (для PostgreSQL с pg_hint_plan):
Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
# SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
# File activerecord/lib/active_record/relation/query_methods.rb, line 850
def or(other)
if other.is_a?(Relation)
spawn.or!(other)
else
raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
end
end Возвращает новое отношение, которое является логическим объединением этого отношения и переданного в качестве аргумента.
Два отношения должны быть структурно совместимы: они должны охватывать одну и ту же модель, и они должны отличаться только по 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))
# File activerecord/lib/active_record/relation/query_methods.rb, line 412
def order(*args)
check_if_method_has_arguments!(__callee__, args) do
sanitize_order_arguments(args)
end
spawn.order!(*args)
end Применяет 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
Пользовательский синтаксис запросов, например, столбцы JSON для Postgres, поддерживается таким образом.
User.order(Arel.sql("payload->>'kind'"))
# SELECT "users".* FROM "users" ORDER BY payload->>'kind'
# File activerecord/lib/active_record/relation/query_methods.rb, line 208 def preload(*args) check_if_method_has_arguments!(__callee__, args) spawn.preload!(*args) end
Позволяет предварительную загрузку args, так же, как это делает includes:
User.preload(:posts) # SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
# File activerecord/lib/active_record/relation/query_methods.rb, line 974 def readonly(value = true) spawn.readonly!(value) end
Устанавливает атрибуты только для чтения для возвращаемого отношения. Если значение равно true (по умолчанию), попытка обновить запись приведет к ошибке.
users = User.readonly users.first.save => ActiveRecord::ReadOnlyRecord: User is marked as readonly
# File activerecord/lib/active_record/relation/query_methods.rb, line 241 def references(*table_names) check_if_method_has_arguments!(__callee__, table_names) spawn.references!(*table_names) end
Используется для указания того, что заданные table_names ссылаются на строку SQL и, следовательно, должны быть объединены в любом запросе, а не загружены отдельно. Этот метод работает только в сочетании с 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
# File activerecord/lib/active_record/relation/query_methods.rb, line 458
def reorder(*args)
check_if_method_has_arguments!(__callee__, args) do
sanitize_order_arguments(args)
end
spawn.reorder!(*args)
end Заменяет любой существующий порядок, определенный в отношении, на указанный порядок.
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'.
# File activerecord/lib/active_record/relation/query_methods.rb, line 319 def reselect(*args) check_if_method_has_arguments!(__callee__, args) spawn.reselect!(*args) end
Позволяет изменить ранее установленный оператор 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.
# File activerecord/lib/active_record/relation/query_methods.rb, line 1163 def reverse_order spawn.reverse_order! end
Обратить существующее предложение order в отношении.
User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
# File activerecord/lib/active_record/relation/query_methods.rb, line 746 def rewhere(conditions) scope = spawn where_clause = scope.build_where_clause(conditions) scope.unscope!(where: where_clause.extract_attributes) scope.where_clause += where_clause scope end
Позволяет изменить ранее установленное условие 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.
# File activerecord/lib/active_record/relation/query_methods.rb, line 291
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.")
spawn._select!(*fields)
end Работает двумя уникальными способами.
Первый: принимает блок, поэтому его можно использовать так же, как 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">]
Вы также можете использовать одну или несколько строк, которые будут использоваться без изменений в качестве полей SELECT.
Model.select('field AS field_one', 'other_field AS field_two')
# => [#<Model id: nil, field: "value", other_field: "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
# File activerecord/lib/active_record/relation/query_methods.rb, line 989 def strict_loading(value = true) spawn.strict_loading!(value) end
Устанавливает возвращаемое отношение в режим strict_loading. Это приведет к ошибке, если запись попытается лениво загрузить ассоциацию.
user = User.strict_loading.first user.comments.to_a => ActiveRecord::StrictLoadingViolationError
# File activerecord/lib/active_record/relation/query_methods.rb, line 804 def structurally_compatible?(other) structurally_incompatible_values_for(other).empty? end
Проверяет, совместимо ли данное отношение структурно с этим отношением, чтобы определить, можно ли использовать методы 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
# File activerecord/lib/active_record/relation/query_methods.rb, line 1204
def uniq!(name)
if values = @values[name]
values.uniq! if values.is_a?(Array) && !values.empty?
end
self
end Удаление дубликатов значений.
# File activerecord/lib/active_record/relation/query_methods.rb, line 511 def unscope(*args) check_if_method_has_arguments!(__callee__, args) spawn.unscope!(*args) end
Удаляет нежелательное отношение, которое уже определено в цепочке отношений. Это полезно, когда вы передаете цепочки отношений и хотите изменить отношения, не перестраивая всю цепочку.
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) }
# File activerecord/lib/active_record/relation/query_methods.rb, line 718
def where(*args)
if args.empty?
WhereChain.new(spawn)
elsif args.length == 1 && args.first.blank?
self
else
spawn.where!(*args)
end
end Возвращает новое отношение, которое является результатом фильтрации текущего отношения в соответствии с условиями в аргументах.
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';
Это может сделать код более читаемым в сложных запросах.
Наконец, вы можете использовать escape-последовательности в стиле sprintf в шаблоне. Это работает немного иначе, чем предыдущие методы; вы отвечаете за то, чтобы значения в шаблоне были правильно экранированы. Значения передаются соединителю для экранирования, но вызывающая сторона отвечает за то, чтобы они были заключены в кавычки в результирующем SQL. После экранирования значения вставляются с использованием тех же escape-последовательностей, что и у основного метода 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)
Объединения
Если отношение является результатом объединения, вы можете создать условие, которое использует любую из таблиц в объединении. Для строковых и массивовых условий используйте имя таблицы в условии.
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, который может быть связан с not для возврата нового отношения, которое отрицает предложение where.
User.where.not(name: "Jon") # SELECT * FROM users WHERE name != 'Jon'
См. WhereChain для получения дополнительной информации о not.
пустое условие
Если условие является любым пустым объектом, то where является операцией без результата и возвращает текущее отношение.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.