Spec-Zone.ru › Ruby on Rails 5.0

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 24
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 661
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 297
def any?
  return super if block_given?
  !empty?
end

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

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

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

build(*args, &block)
Псевдоним для: new
cache_key(timestamp_column = :updated_at) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 335
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 149
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 159
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 572
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(conditions = nil) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 516
    def delete_all(conditions = nil)
      invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
        if MULTI_VALUE_METHODS.include?(method)
          send("#{method}_values").any?
        elsif SINGLE_VALUE_METHODS.include?(method)
          send("#{method}_value")
        elsif CLAUSE_METHODS.include?(method)
          send("#{method}_clause").any?
        end
      }
      if invalid_methods.any?
        raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
      end

      if conditions
        ActiveSupport::Deprecation.warn("          Passing conditions to delete_all is deprecated and will be removed in Rails 5.1.
          To achieve the same use where(conditions).delete_all.
".squish)
        where(conditions).delete_all
      else
        stmt = Arel::DeleteManager.new
        stmt.from(table)

        if joins_values.any?
          @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
    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 490
def destroy(id)
  if id.is_a?(Array)
    id.map { |one_id| destroy(one_id) }
  else
    find(id).destroy
  end
end

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

В сущности, это находит объект (или несколько объектов) с заданным идентификатором, создает новый объект из атрибутов и затем вызывает destroy для него.

Параметры

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

Примеры

# Destroy a single object
Todo.destroy(1)

# Destroy multiple objects
todos = [1,2,3]
Todo.destroy(todos)
destroy_all(conditions = nil) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 459
    def destroy_all(conditions = nil)
      if conditions
        ActiveSupport::Deprecation.warn("          Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1.
          To achieve the same use where(conditions).destroy_all.
".squish)
        where(conditions).destroy_all
      else
        records.each(&:destroy).tap { reset }
      end
    end

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

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

Примеры

Person.where(age: 0..18).destroy_all
eager_loading?() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 637
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 279
def empty?
  return @records.empty? if loaded?

  if limit_value == 0
    true
  else
    c = count(:all)
    c.respond_to?(:zero?) ? c.zero? : c.empty?
  end
end

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

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

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

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

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

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

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

find_or_create_by(attributes, &block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 223
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 230
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 236
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 33
def initialize_copy(other)
  # This method is a hot spot, so for now, use Hash[] to dup the hash.
  #   https://bugs.ruby-lang.org/issues/7166
  @values        = Hash[@values]
  reset
end
inspect() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 685
def inspect
  entries = records.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 647
def joined_includes_values
  includes_values & joins_values
end

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

load(&block) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 582
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 309
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 124
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 291
def none?
  return super if block_given?
  empty?
end

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

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

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

reset() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 594
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 632
def scope_for_create
  @scope_for_create ||= where_values_hash.merge(create_with_value)
end
scoping() { || ... } Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 349
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 274
def size
  loaded? ? @records.length : count(:all)
end

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

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

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

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

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

                binds = relation.bound_attributes
                binds = connection.prepare_binds_for_database(binds)
                binds.map! { |value| connection.quote(value) }
                collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new)
                collect.substitute_binds(binds).join
              end
end

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

User.where(name: 'Oscar').to_sql
# => SELECT "users".* FROM "users"  WHERE "users"."name" = 'Oscar'
uniq_value() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 655
def uniq_value
  distinct_value
end

#uniq и #uniq! устарели. uniq_value делегирует distinct_value для сохранения обратной совместимости. Используйте distinct_value вместо этого.

update(id = :all, attributes) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 424
    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
          id = id.id
          ActiveSupport::Deprecation.warn("            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 - Это должен быть идентификатор или массив идентификаторов для обновления.

  • 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 378
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 joins_values.any?
    @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 681
def values
  Hash[@values]
end
where_values_hash(relation_table_name = table_name) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 628
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 698
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