Spec-Zone.ru › Ruby on Rails 5.2

class ActiveRecord::Relation

Parent:
Object
Included modules:
Enumerable, ActiveRecord::FinderMethods, ActiveRecord::Calculations, ActiveRecord::SpawnMethods, ActiveRecord::QueryMethods, ActiveRecord::Batches, ActiveRecord::Explain

Active Record Relation

Постоянные

CLAUSE_METHODS
INVALID_METHODS_FOR_DELETE_ALL
MULTI_VALUE_METHODS
SINGLE_VALUE_METHODS
VALUE_METHODS

Атрибуты

klass[R]
loaded[R]
loaded?[R]
model[R]
predicate_builder[R]
table[R]

Публичные методы класса

new(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {}) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 25
def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
  @klass  = klass
  @table  = table
  @values = values
  @offsets = {}
  @loaded = false
  @predicate_builder = predicate_builder
  @delegate_to_klass = false
end

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

==(other) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 487
def ==(other)
  case other
  when Associations::CollectionProxy, AssociationRelation
    self == other.records
  when Relation
    other.to_sql == to_sql
  when Array
    records == other
  end
end

Сравнивает два отношения на равенство.

any?() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 227
def any?
  return super if block_given?
  !empty?
end

Возвращает true, если существуют какие-либо записи.

Вызывает метод суперкласса
blank?() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 503
def blank?
  records.blank?
end

Возвращает true, если отношение пустое.

build(attributes = nil, &block)
Псевдоним для: new
cache_key(timestamp_column = :updated_at) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 265
def cache_key(timestamp_column = :updated_at)
  @cache_keys ||= {}
  @cache_keys[timestamp_column] ||= @klass.collection_cache_key(self, timestamp_column)
end

Возвращает ключ кэша, который может быть использован для идентификации записей, полученных этим запросом. Ключ кэша строится с отпечатком sql запроса, количеством записей, соответствующих запросу и отметкой времени последней обновлённой записи. Когда появляется новая запись, соответствующая запросу, или любая из существующих записей обновляется или удаляется, ключ кэша изменяется.

Product.where("name like ?", "%Cosmic Encounter%").cache_key
# => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"

Если коллекция загружена, метод будет перебирать записи для получения отметки времени, иначе он запустит один sql запрос, например:

SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')

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

Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)

Вы можете настроить стратегию генерации ключа на основе модели, переопределяя ActiveRecord::Base#collection_cache_key.

create(attributes = nil, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 81
def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    scoping { klass.create(values_for_create(attributes), &block) }
  end
end

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

Ожидает аргументы в том же формате, что и ActiveRecord::Base.create.

Примеры

users = User.where(name: 'Oscar')
users.create # => #<User id: 3, name: "Oscar", ...>

users.create(name: 'fxn')
users.create # => #<User id: 4, name: "fxn", ...>

users.create { |user| user.name = 'tenderlove' }
# => #<User id: 5, name: "tenderlove", ...>

users.create(name: nil) # validation on name
# => #<User id: nil, name: nil, ...>
create!(attributes = nil, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 95
def create!(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create!(attr, &block) }
  else
    scoping { klass.create!(values_for_create(attributes), &block) }
  end
end

Аналогично create, но вызывает create! в базовом классе. Вызывает исключение, если возникает ошибка валидации.

Ожидает аргументы в том же формате, что и ActiveRecord::Base.create!.

delete_all() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 386
def delete_all
  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
    value = get_value(method)
    SINGLE_VALUE_METHODS.include?(method) ? value : value.any?
  end
  if invalid_methods.any?
    raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
  end

  if eager_loading?
    relation = apply_join_dependency
    return relation.delete_all
  end

  stmt = Arel::DeleteManager.new
  stmt.from(table)

  if has_join_values? || has_limit_or_offset?
    @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
  else
    stmt.wheres = arel.constraints
  end

  affected = @klass.connection.delete(stmt, "#{@klass} Destroy")

  reset
  affected
end

Удаляет записи без предварительной инициализации записей, а значит, не вызывая метод #destroy и не вызывая обратные вызовы. Это единственное SQL-утверждение DELETE, которое идёт напрямую в базу данных, гораздо более эффективно, чем destroy_all. Будьте внимательны с отношениями, в частности :dependent правила, определённые для ассоциаций, не соблюдаются. Возвращает количество изменённых строк.

Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all

Оба вызова удаляют затронутые записи сразу с помощью одного оператора DELETE. Если вам нужно уничтожить зависимые ассоциации или вызвать свои before_* или after_destroy обратные вызовы, используйте метод destroy_all вместо него.

Если предоставлен неверный метод, delete_all вызывает исключение ActiveRecordError:

Post.distinct.delete_all
# => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
destroy_all() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 364
def destroy_all
  records.each(&:destroy).tap { reset }
end

Уничтожает записи, инициализируя каждую запись и вызывая её метод #destroy. Обратные вызовы каждого объекта выполняются (включая :dependent параметры ассоциации). Возвращает коллекцию объектов, которые были уничтожены; каждый из них будет заморожен, чтобы отразить, что никаких изменений делать нельзя (так как их нельзя сохранить).

Примечание: Инициализация, выполнение обратного вызова и удаление каждой записи может быть длительным при удалении большого количества записей сразу. Он генерирует по крайней мере один SQL DELETE запрос на запись (или возможно больше, чтобы обеспечить соблюдение ваших обратных вызовов). Если вам нужно быстро удалить много строк, не беспокоясь о их ассоциациях или обратных вызовах, используйте delete_all вместо этого.

Примеры

Person.where(age: 0..18).destroy_all
eager_loading?() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 472
def eager_loading?
  @should_eager_load ||=
    eager_load_values.any? ||
    includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
end

Возвращает true, если отношение требует жадного загрузки.

empty?() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 215
def empty?
  return @records.empty? if loaded?
  !exists?
end

Возвращает true, если нет записей.

encode_with(coder) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 205
def encode_with(coder)
  coder.represent_seq(nil, records)
end

Сериализует объекты отношения Массив.

explain() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 189
def explain
  exec_explain(collecting_queries_for_explain { exec_queries })
end

Выполняет EXPLAIN для запроса или запросов, вызываемых этим отношением, и возвращает результат в виде строки. Строка отформатирована, имитируя строки, выводимые оболочкой базы данных.

Обратите внимание, что этот метод фактически выполняет запросы, так как результаты некоторых из них необходимы следующим, при выполнении жадной загрузки.

Дополнительные сведения см. в Руководстве Active Record Query Interface.

find_or_create_by(attributes, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 163
def find_or_create_by(attributes, &block)
  find_by(attributes) || create(attributes, &block)
end

Находит первую запись с заданными атрибутами или создаёт запись с атрибутами, если она не найдена:

# Find the first user named "Penélope" or create a new one.
User.find_or_create_by(first_name: 'Penélope')
# => #<User id: 1, first_name: "Penélope", last_name: nil>

# Find the first user named "Penélope" or create a new one.
# We already have one so the existing record will be returned.
User.find_or_create_by(first_name: 'Penélope')
# => #<User id: 1, first_name: "Penélope", last_name: nil>

# Find the first user named "Scarlett" or create a new one with
# a particular last name.
User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
# => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">

Этот метод принимает блок, который передаётся методу create. Последний пример выше можно альтернативно записать так:

# Find the first user named "Scarlett" or create a new one with a
# different last name.
User.find_or_create_by(first_name: 'Scarlett') do |user|
  user.last_name = 'Johansson'
end
# => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">

Этот метод всегда возвращает запись, но если попытка создания завершилась неудачей из-за ошибок валидации, она не будет сохранена, вы получите то, что возвращает create в такой ситуации.

Обратите внимание, *что этот метод не атомарный*, он сначала выполняет SELECT, а если результатов нет, то пытается выполнить INSERT. Если есть другие потоки или процессы, возникает проблема гонки между обоими вызовами, и в результате может появиться две похожие записи.

Является ли это проблемой или нет, зависит от логики приложения, но в том частном случае, когда строки имеют ограничение UNIQUE, может быть вызвано исключение, просто повторите попытку:

begin
  CreditAccount.transaction(requires_new: true) do
    CreditAccount.find_or_create_by(user_id: user.id)
  end
rescue ActiveRecord::RecordNotUnique
  retry
end
find_or_create_by!(attributes, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 170
def find_or_create_by!(attributes, &block)
  find_by(attributes) || create!(attributes, &block)
end

Как find_or_create_by, но вызывает create!, поэтому исключение выбрасывается, если созданная запись недействительна.

find_or_initialize_by(attributes, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 176
def find_or_initialize_by(attributes, &block)
  find_by(attributes) || new(attributes, &block)
end

Подобно find_or_create_by, но вызывает new вместо create.

initialize_copy(other) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 35
def initialize_copy(other)
  @values = @values.dup
  reset
end
inspect() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 511
def inspect
  subject = loaded? ? records : self
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)

  entries[10] = "..." if entries.size == 11

  "#<#{self.class.name} [#{entries.join(', ')}]>"
end
joined_includes_values() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 482
def joined_includes_values
  includes_values & joins_values
end

Объединяет запросы, которые также помечены для предварительной загрузки. В этом случае мы должны просто загрузить их с немедленной подгрузкой. Обратите внимание, что это упрощенное решение, потому что у нас могут быть строки и символы, которые представляют одно и то же отношение, но не совпадают с этим. Также могут быть вложенные хеши, которые частично совпадают, например { a: :b } и { a: [:b, :c] }

load(&block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 421
def load(&block)
  exec_queries(&block) unless loaded?

  self
end

Загружает записи из базы данных, если они еще не загружены. Вы можете использовать это, если по какой-то причине вам нужно явно загрузить некоторые записи перед их фактическим использованием. Возвращаемое значение — это само отношение, а не записи.

Post.where(published: true).load # => #<ActiveRecord::Relation>
many?() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 239
def many?
  return super if block_given?
  limit_value ? records.many? : size > 1
end

Возвращает true, если записей более одной.

Вызывает метод родительского класса Enumerable#many?
new(attributes = nil, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 56
def new(attributes = nil, &block)
  scoping { klass.new(values_for_create(attributes), &block) }
end

Инициализирует новую запись из отношения, сохраняя текущий контекст.

Ожидает аргументы в том же формате, что и ActiveRecord::Base.new.

users = User.where(name: 'DHH')
user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>

Также можно передать блок в new, с новой записью в качестве аргумента:

user = users.new { |user| user.name = 'Oscar' }
user.name # => Oscar
Также алиас: build
none?() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 221
def none?
  return super if block_given?
  empty?
end

Возвращает true, если записей нет.

Вызывает метод родительского класса
one?() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 233
def one?
  return super if block_given?
  limit_value ? records.one? : size == 1
end

Возвращает true, если ровно одна запись.

Вызывает метод родительского класса
pretty_print(q) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 498
def pretty_print(q)
  q.pp(records)
end
reload() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 428
def reload
  reset
  load
end

Вынуждает перезагрузку отношения.

reset() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 433
def reset
  @delegate_to_klass = false
  @to_sql = @arel = @loaded = @should_eager_load = nil
  @records = [].freeze
  @offsets = {}
  self
end
scope_for_create() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 467
def scope_for_create
  where_values_hash.merge!(create_with_value.stringify_keys)
end
scoping() { || ... } Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 279
def scoping
  previous, klass.current_scope = klass.current_scope(true), self unless @delegate_to_klass
  yield
ensure
  klass.current_scope = previous unless @delegate_to_klass
end

Применяет все запросы к текущему контексту.

Comment.where(post_id: 1).scoping do
  Comment.first
end
# => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1

Пожалуйста, используйте unscoped, если хотите удалить все предыдущие контексты (включая default_scope) во время выполнения блока.

size() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 210
def size
  loaded? ? @records.length : count(:all)
end

Возвращает размер коллекции записей.

to_a()
Псевдоним для: to_ary
to_ary() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 194
def to_ary
  records.dup
end

Преобразует объекты отношения в Массив.

Также алиас: to_a
to_sql() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 445
def to_sql
  @to_sql ||= begin
    if eager_loading?
      apply_join_dependency do |relation, join_dependency|
        relation = join_dependency.apply_column_aliases(relation)
        relation.to_sql
      end
    else
      conn = klass.connection
      conn.unprepared_statement { conn.to_sql(arel) }
    end
  end
end

Возвращает SQL-запрос для отношения.

User.where(name: 'Oscar').to_sql
# => SELECT "users".* FROM "users"  WHERE "users"."name" = 'Oscar'
update_all(updates) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 315
def update_all(updates)
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?

  if eager_loading?
    relation = apply_join_dependency
    return relation.update_all(updates)
  end

  stmt = Arel::UpdateManager.new

  stmt.set Arel.sql(@klass.sanitize_sql_for_assignment(updates))
  stmt.table(table)

  if has_join_values? || offset_value
    @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
  else
    stmt.key = arel_attribute(primary_key)
    stmt.take(arel.limit)
    stmt.order(*arel.orders)
    stmt.wheres = arel.constraints
  end

  @klass.connection.update stmt, "#{@klass} Update All"
end

Обновляет все записи в текущем отношении с указанными деталями. Этот метод создает единственное SQL-UPDATE-выражение и отправляет его напрямую в базу данных. Он не создает экземпляры вовлеченных моделей и не вызывает обратные вызовы или валидацию Active Record. Тем не менее, значения, переданные в update_all, всё равно пройдут через обычное преобразование типов и сериализацию Active Record.

Параметры

  • updates — Строка, массив или хеш, представляющий часть SQL-запроса SET.

Примеры

# Update all customers with the given attributes
Customer.update_all wants_email: true

# Update all books with 'Rails' in their title
Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')

# Update all books that match conditions, but limit it to 5 ordered by date
Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')

# Update all invoices and set the number column to its id value.
Invoice.update_all('number = id')
values() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 507
def values
  @values.dup
end
where_values_hash(relation_table_name = klass.table_name) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 463
def where_values_hash(relation_table_name = klass.table_name)
  where_clause.to_h(relation_table_name)
end

Возвращает хеш условий WHERE.

User.where(name: 'Oscar').where_values_hash
# => {name: "Oscar"}

Защищенные методы экземпляра

load_records(records) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 535
def load_records(records)
  @records = records.freeze
  @loaded = true
end

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

Spec-Zone.ru

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