Spec-Zone.ru › Ruby on Rails 5.1

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, predicate_builder, values = {}) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 22
def initialize(klass, table, predicate_builder, values = {})
  @klass  = klass
  @table  = table
  @values = values
  @offsets = {}
  @loaded = false
  @predicate_builder = predicate_builder
end

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

==(other) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 612
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 282
def any?
  return super if block_given?
  !empty?
end

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

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

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

build(*args, &block)
Псевдоним для: new
cache_key(timestamp_column = :updated_at) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 320
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(*args, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 145
def create(*args, &block)
  scoping { @klass.create(*args, &block) }
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!(*args, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 155
def create!(*args, &block)
  scoping { @klass.create!(*args, &block) }
end

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

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

delete(id_or_array) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 535
def delete(id_or_array)
  where(primary_key => id_or_array).delete_all
end

Удаляет строку с первичным ключом, соответствующим аргументу id, используя SQL DELETE оператор и возвращает количество удалённых строк. Объекты Active Record не создаются, поэтому не выполняются обратные вызовы объекта, включая любые :dependent параметры ассоциации.

Можно удалить несколько строк сразу, передав массив Array id.

Примечание: Хотя это часто намного быстрее, чем альтернатива, destroy, пропуск обратных вызовов может обойти бизнес-логику в вашем приложении, которая обеспечивает целостность ссылок или выполняет другие важные задачи.

Примеры

# Delete a single row
Todo.delete(1)

# Delete multiple rows
Todo.delete([2,3,4])
delete_all() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 492
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

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

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

  affected = @klass.connection.delete(stmt, "SQL", bound_attributes)

  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.limit(100).delete_all
# => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
destroy(id) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 466
def destroy(id)
  if id.is_a?(Array)
    id.map { |one_id| destroy(one_id) }
  else
    find(id).destroy
  end
end

Уничтожает объект (или несколько объектов) с заданным идентификатором. Объект инициализируется вначале, поэтому все обратные вызовы и фильтры запускаются перед удалением объекта. Этот метод менее эффективен, чем delete, но позволяет запускать методы очистки и другие действия.

По сути, этот метод находит объект (или несколько объектов) с заданным id, создаёт новый объект из атрибутов и затем вызывает destroy на нём.

Параметры

  • id - Может быть целым числом Integer или массивом Array целых чисел.

Примеры

# Destroy a single object
Todo.destroy(1)

# Destroy multiple objects
todos = [1,2,3]
Todo.destroy(todos)
destroy_all() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 443
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 597
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 270
def empty?
  return @records.empty? if loaded?
  !exists?
end

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

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

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

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

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

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

Дополнительные сведения см. в руководстве по интерфейсу запросов Active Record.

find_or_create_by(attributes, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 219
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 226
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 232
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 31
def initialize_copy(other)
  @values = @values.dup
  reset
end
inspect() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 636
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 607
def joined_includes_values
  includes_values & joins_values
end

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

load(&block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 545
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 294
def many?
  return super if block_given?
  limit_value ? records.many? : size > 1
end

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

Вызывает метод суперкласса Enumerable#many?
new(*args, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 120
def new(*args, &block)
  scoping { @klass.new(*args, &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 276
def none?
  return super if block_given?
  empty?
end

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

Вызывает метод суперкласса
one?() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 288
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 623
def pretty_print(q)
  q.pp(records)
end
reload() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 552
def reload
  reset
  load
end

Принудительно перезагружает отношение.

reset() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 557
def reset
  @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
  @should_eager_load = @join_dependency = nil
  @records = [].freeze
  @offsets = {}
  self
end
scope_for_create() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 592
def scope_for_create
  @scope_for_create ||= where_values_hash.merge(create_with_value)
end
scoping() { || ... } Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 334
def scoping
  previous, klass.current_scope = klass.current_scope(true), self
  yield
ensure
  klass.current_scope = previous
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 265
def size
  loaded? ? @records.length : count(:all)
end

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

to_a() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 250
def to_a
  records.dup
end

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

to_sql() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 569
def to_sql
  @to_sql ||= begin
                relation = self

                if eager_loading?
                  find_with_associations { |rel, _| relation = rel }
                end

                conn = klass.connection
                conn.unprepared_statement {
                  conn.to_sql(relation.arel, relation.bound_attributes)
                }
              end
end

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

User.where(name: 'Oscar').to_sql
# => SELECT "users".* FROM "users"  WHERE "users"."name" = 'Oscar'
update(id = :all, attributes) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 409
    def update(id = :all, attributes)
      if id.is_a?(Array)
        id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
      elsif id == :all
        records.each { |record| record.update(attributes) }
      else
        if ActiveRecord::Base === id
          raise ArgumentError, "            You are passing an instance of ActiveRecord::Base to `update`.
            Please pass the id of the object by calling `.id`.
".squish
        end
        object = find(id)
        object.update(attributes)
        object
      end
    end

Обновляет объект (или несколько объектов) и сохраняет его в базе данных, если пройдут проверки. Результирующий объект возвращается независимо от того, был ли объект успешно сохранен в базе данных или нет.

Параметры

  • id - Это должен быть id или массив id для обновления.

  • attributes - Это должен быть хэш атрибутов или массив хэшей.

Примеры

# Updates one record
Person.update(15, user_name: 'Samuel', group: 'expert')

# Updates multiple records
people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
Person.update(people.keys, people.values)

# Updates multiple records from the result of a relation
people = Person.where(group: 'expert')
people.update(group: 'masters')

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

update_all(updates) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 363
def update_all(updates)
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?

  stmt = Arel::UpdateManager.new

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

  if has_join_values?
    @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, "SQL", bound_attributes
end

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

Параметры

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

Примеры

# 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 632
def values
  @values.dup
end
where_values_hash(relation_table_name = table_name) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 588
def where_values_hash(relation_table_name = 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 655
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