Spec-Zone.ru › Ruby on Rails 7.0

module ActiveRecord::QueryMethods

Константы

FROZEN_EMPTY_ARRAY
FROZEN_EMPTY_HASH
VALID_UNSCOPING_VALUES

Открытые методы экземпляра

and(other) Показать исходный код
# 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)
annotate(*args) Показать исходный код
# 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, «/*» и «*/», будут добавлены автоматически.

create_with(value) Показать исходный код
# 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'
distinct(value = true) Показать исходный код
# 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
eager_load(*args) Показать исходный код
# 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"
excluding(*records) Показать исходный код
# 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 будет выброшено, если не указаны записи или если какие-либо записи в коллекции (если передана коллекция) не являются экземплярами той же модели, которую охватывает отношение.

Также называется как: without
extending(*modules, &block) Показать исходный код
# 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
extract_associated(association) Показать исходный код
# 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)
from(value, subquery_name = nil) Показать исходный код
# 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')
group(*args) Показать исходный код
# 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">]
having(opts, *rest) Показать исходный код
# 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')
in_order_of(column, values) Показать исходный код
# 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)
includes(*args) Показать исходный код
# 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' })
invert_where() Показать исходный код
# 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)
joins(*args) Показать исходный код
# 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
left_joins(*args)
Псевдоним для: left_outer_joins
left_outer_joins(*args) Показать исходный код
# 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"
Также имеет псевдоним: left_joins
limit(value) Показать исходный код
# 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'
lock(locks = true) Показать исходный код
# File activerecord/lib/active_record/relation/query_methods.rb, line 917
def lock(locks = true)
  spawn.lock!(locks)
end

Задает параметры блокировки (по умолчанию true). Для получения дополнительной информации о блокировке см. ActiveRecord::Locking.

none() Показать исходный код
# 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
offset(value) Показать исходный код
# 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")
optimizer_hints(*args) Показать исходный код
# 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"
or(other) Показать исходный код
# 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))
order(*args) Показать исходный код
# 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'
preload(*args) Показать исходный код
# 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)
readonly(value = true) Показать исходный код
# 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
references(*table_names) Показать исходный код
# 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
reorder(*args) Показать исходный код
# 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'.

reselect(*args) Показать исходный код
# 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.

reverse_order() Показать исходный код
# 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'
rewhere(conditions) Показать исходный код
# 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.

select(*fields) Show source
# 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
Вызывает метод суперкласса
strict_loading(value = true) Show source
# 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
structurally_compatible?(other) Show source
# 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
uniq!(name) Show source
# 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

Удаление дубликатов значений.

unscope(*args) Show source
# 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) }
where(*args) Show source
# 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 является операцией без результата и возвращает текущее отношение.

without(*records)
Alias for: excluding

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API