Spec-Zone.ru › Ruby on Rails 4.1

модуль ActiveRecord::QueryMethods

Константы

VALID_UNSCOPING_VALUES

Общедоступные методы экземпляра

bind(value) Показать исходный код
# File activerecord/lib/active_record/relation/query_methods.rb, line 425
def bind(value)
  spawn.bind!(value)
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'
# File activerecord/lib/active_record/relation/query_methods.rb, line 715
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
# File activerecord/lib/active_record/relation/query_methods.rb, line 762
def distinct(value = true)
  spawn.distinct!(value)
end
Также алиасирован как: uniq
eager_load(*args) Показать исходный код

Принудительно загружает данные жадно, выполняя 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 150
def eager_load(*args)
  check_if_method_has_arguments!(:eager_load, args)
  spawn.eager_load!(*args)
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
# File activerecord/lib/active_record/relation/query_methods.rb, line 810
def extending(*modules, &block)
  if modules.any? || block
    spawn.extending!(*modules, &block)
  else
    self
  end
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

Topic.select('a.title').from(Topic.approved, :a)
# => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
# File activerecord/lib/active_record/relation/query_methods.rb, line 743
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, ...>]
# File activerecord/lib/active_record/relation/query_methods.rb, line 269
def group(*args)
  check_if_method_has_arguments!(:group, args)
  spawn.group!(*args)
end
having(opts, *rest) Показать исходный код

Позволяет указать условие HAVING. Обратите внимание, что вы не можете использовать HAVING без указания условия GROUP.

Order.having('SUM(price) > 30').group('user_id')
# File activerecord/lib/active_record/relation/query_methods.rb, line 593
def having(opts, *rest)
  opts.blank? ? self : spawn.having!(opts, *rest)
end
includes(*args) Показать исходный код

Указывает отношения, которые необходимо включить в набор результатов. Например:

users = User.includes(:address)
users.each do |user|
  user.address.city
end

позволяет получить доступ к атрибуту address модели User без выполнения дополнительного запроса. Это часто приводит к улучшению производительности по сравнению с простым join.

Вы также можете указать несколько отношений, например так:

users = User.includes(:address, :friends)

Загрузка вложенных отношений возможна с помощью хэша:

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 требует фактического имени таблицы.

# File activerecord/lib/active_record/relation/query_methods.rb, line 131
def includes(*args)
  check_if_method_has_arguments!(:includes, args)
  spawn.includes!(*args)
end
joins(*args) Показать исходный код

Выполняет joins на args.

User.joins(:posts)
=> SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."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
# File activerecord/lib/active_record/relation/query_methods.rb, line 411
def joins(*args)
  check_if_method_has_arguments!(:joins, args)

  args.compact!
  args.flatten!

  spawn.joins!(*args)
end
limit(value) Показать исходный код

Устанавливает лимит на количество извлекаемых записей.

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 609
def limit(value)
  spawn.limit!(value)
end
lock(locks = true) Показать исходный код

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

# File activerecord/lib/active_record/relation/query_methods.rb, line 636
def lock(locks = true)
  spawn.lock!(locks)
end
none() Показать исходный код

Возвращает цепочный объект отношения с нулевым количеством записей.

Возвращаемое отношение реализует шаблон 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 679
def none
  where("1=0").extending!(NullRelation)
end
offset(value) Показать исходный код

Указывает количество строк, которое необходимо пропустить, прежде чем возвращать строки.

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 625
def offset(value)
  spawn.offset!(value)
end
order(*args) Показать исходный код

Позволяет указать атрибут сортировки:

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

User.order(:name)
=> SELECT "users".* FROM "users" ORDER BY "users"."name" ASC

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
# File activerecord/lib/active_record/relation/query_methods.rb, line 300
def order(*args)
  check_if_method_has_arguments!(:order, args)
  spawn.order!(*args)
end
preload(*args) Показать исходный код

Позволяет предварительно загрузить 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 164
def preload(*args)
  check_if_method_has_arguments!(:preload, args)
  spawn.preload!(*args)
end
readonly(value = true) Показать исходный код

Устанавливает атрибуты только для чтения для возвращаемого отношения. Если значение равно true (по умолчанию), попытка обновления записи приведет к ошибке.

users = User.readonly
users.first.save
=> ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
# File activerecord/lib/active_record/relation/query_methods.rb, line 693
def readonly(value = true)
  spawn.readonly!(value)
end
references(*table_names) Показать исходный код

Указывает, что данные 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 184
def references(*table_names)
  check_if_method_has_arguments!(:references, table_names)
  spawn.references!(*table_names)
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'.

# File activerecord/lib/active_record/relation/query_methods.rb, line 321
def reorder(*args)
  check_if_method_has_arguments!(:reorder, args)
  spawn.reorder!(*args)
end
reverse_order() Показать исходный код

Инвертирует существующее условие порядка в отношении.

User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
# File activerecord/lib/active_record/relation/query_methods.rb, line 831
def reverse_order
  spawn.reverse_order!
end
END_OF_DOCUMENT_MARKER
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.

# File activerecord/lib/active_record/relation/query_methods.rb, line 585
def rewhere(conditions)
  unscope(where: conditions.keys).where(conditions)
end
select(*fields) { |*block_args| ... } Показать исходный код

Работает двумя уникальными способами.

Во-первых: принимает блок, так что его можно использовать как Array#select.

Model.all.select { |m| m.field == value }

Это создаст массив объектов из базы данных для области видимости, преобразуя их в массив и итерируясь по ним с помощью Array#select.

Во-вторых: изменяет оператор SELECT для запроса, чтобы извлекались только определённые поля:

Model.select(:field)
# => [#<Model field:value>]

Хотя на примере кажется, что этот метод возвращает массив, на самом деле он возвращает объект типа relation, и к нему можно добавить другие методы запроса, такие как другие методы в ActiveRecord::QueryMethods.

Аргумент метода также может быть массивом полей.

Model.select(:field, :other_field, :and_one_more)
# => [#<Model field: "value", other_field: "value", and_one_more: "value">]

Вы также можете использовать одну или несколько строк, которые будут использоваться без изменений как поля SELECT.

Model.select('field AS field_one', 'other_field AS field_two')
# => [#<Model field: "value", other_field: "value">]

Если был указан псевдоним, он будет доступен из полученных объектов:

Model.select('field AS field_one').first.field_one
# => "value"

Обращение к атрибутам объекта, для которых не извлечены поля с помощью select, вызовет ActiveModel::MissingAttributeError:

Model.select(:field).first.other_field
# => ActiveModel::MissingAttributeError: missing attribute: other_field
# File activerecord/lib/active_record/relation/query_methods.rb, line 236
def select(*fields)
  if block_given?
    to_a.select { |*block_args| yield(*block_args) }
  else
    raise ArgumentError, 'Call this with at least one field' if fields.empty?
    spawn._select!(*fields)
  end
end
uniq(value = true)
Псевдоним для: distinct
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 }
# File activerecord/lib/active_record/relation/query_methods.rb, line 371
def unscope(*args)
  check_if_method_has_arguments!(:unscope, args)
  spawn.unscope!(*args)
end
where(opts = :chain, *rest) Показать исходный код

Возвращает новое отношение, которое является результатом фильтрации текущего отношения в соответствии с условиями в аргументах.

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)

Соединения

Если отношение является результатом соединения, вы можете создать условие, которое использует любую из таблиц в соединении. Для строчных и массивно-условных значений используйте имя таблицы в условии.

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 ничего не делает и возвращает текущее отношение.

# File activerecord/lib/active_record/relation/query_methods.rb, line 553
def where(opts = :chain, *rest)
  if opts == :chain
    WhereChain.new(spawn)
  elsif opts.blank?
    self
  else
    spawn.where!(opts, *rest)
  end
end

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

Spec-Zone.ru

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