Spec-Zone.ru › Ruby on Rails 4.1

class ActiveRecord::Relation

Parent:
Объект
Included modules:
ActiveRecord::FinderMethods, ActiveRecord::Calculations, ActiveRecord::SpawnMethods, ActiveRecord::QueryMethods, ActiveRecord::Batches, ActiveRecord::Explain, ActiveRecord::Delegation

Active Record Отношение

Константы

JoinOperation
MULTI_VALUE_METHODS
SINGLE_VALUE_METHODS
VALUE_METHODS

Атрибуты

klass[Ч]
loaded[Ч]
loaded?[Ч]
model[Ч]
table[Ч]

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

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

Методы общего использования экземпляров

==(other) Показать исходный код

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

# File activerecord/lib/active_record/relation.rb, line 576
def ==(other)
  case other
  when Associations::CollectionProxy, AssociationRelation
    self == other.to_a
  when Relation
    other.to_sql == to_sql
  when Array
    to_a == other
  end
end
any?() { |*block_args| ... } Показать исходный код

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

# File activerecord/lib/active_record/relation.rb, line 263
def any?
  if block_given?
    to_a.any? { |*block_args| yield(*block_args) }
  else
    !empty?
  end
end
blank?() Показать исходный код

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

# File activerecord/lib/active_record/relation.rb, line 592
def blank?
  to_a.blank?
end
build(*args, &block)
Псевдоним для: new
create(*args, &block) Показать исходный код

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

Ожидаются аргументы в том же формате, что и 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, ...>
# File activerecord/lib/active_record/relation.rb, line 137
def create(*args, &block)
  scoping { @klass.create(*args, &block) }
end
create!(*args, &block) Показать исходный код

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

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

# File activerecord/lib/active_record/relation.rb, line 145
def create!(*args, &block)
  scoping { @klass.create!(*args, &block) }
end
delete(id_or_array) Показать исходный код

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

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

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

Примеры

# Delete a single row
Todo.delete(1)

# Delete multiple rows
Todo.delete([2,3,4])
# File activerecord/lib/active_record/relation.rb, line 481
def delete(id_or_array)
  where(primary_key => id_or_array).delete_all
end
delete_all(conditions = nil) Показать исходный код

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

Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all

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

Если предоставлен ограничитель, delete_all генерирует ошибку ActiveRecord:

Post.limit(100).delete_all
# => ActiveRecord::ActiveRecordError: delete_all doesn't support limit scope
# File activerecord/lib/active_record/relation.rb, line 440
def delete_all(conditions = nil)
  raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value

  if conditions
    where(conditions).delete_all
  else
    stmt = Arel::DeleteManager.new(arel.engine)
    stmt.from(table)

    if joins_values.any?
      @klass.connection.join_to_delete(stmt, arel, table[primary_key])
    else
      stmt.wheres = arel.constraints
    end

    affected = @klass.connection.delete(stmt, 'SQL', bind_values)

    reset
    affected
  end
end
destroy(id) Показать исходный код

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

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

Параметры

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

Примеры

# Destroy a single object
Todo.destroy(1)

# Destroy multiple objects
todos = [1,2,3]
Todo.destroy(todos)
# File activerecord/lib/active_record/relation.rb, line 413
def destroy(id)
  if id.is_a?(Array)
    id.map { |one_id| destroy(one_id) }
  else
    find(id).destroy
  end
end
destroy_all(conditions = nil) Показать исходный код

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

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

Параметры

  • conditions - Строка, массив или хеш, определяющие, какие записи уничтожить. Если опущен, все записи уничтожаются. Дополнительная информация по условиям содержится в разделе Условия во введении к ActiveRecord::Base.

Примеры

Person.destroy_all("last_login < '2004-04-04'")
Person.destroy_all(status: "inactive")
Person.where(age: 0..18).destroy_all
# File activerecord/lib/active_record/relation.rb, line 386
def destroy_all(conditions = nil)
  if conditions
    where(conditions).destroy_all
  else
    to_a.each {|object| object.destroy }.tap { reset }
  end
end
eager_loading?() Показать исходный код

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

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

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

# File activerecord/lib/active_record/relation.rb, line 251
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
explain() Показать исходный код

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

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

Дополнительную информацию см. в руководстве Active Record Query Interface.

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

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

# 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.find_or_create_by(user_id: user.id)
rescue ActiveRecord::RecordNotUnique
  retry
end
# File activerecord/lib/active_record/relation.rb, line 207
def find_or_create_by(attributes, &block)
  find_by(attributes) || create(attributes, &block)
end
find_or_create_by!(attributes, &block) Показать исходный код

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

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

Как find_or_create_by, но вызывает new вместо create.

# File activerecord/lib/active_record/relation.rb, line 218
def find_or_initialize_by(attributes, &block)
  find_by(attributes) || new(attributes, &block)
end
initialize_copy(other) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 30
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]
  @values[:bind] = @values[:bind].dup if @values.key? :bind
  reset
end
inspect() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 600
def inspect
  entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
  entries[10] = '...' if entries.size == 11

  "#<#{self.class.name} [#{entries.join(', ')}]>"
end
joined_includes_values() Показать исходный код

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

# File activerecord/lib/active_record/relation.rb, line 565
def joined_includes_values
  includes_values & joins_values
end
load() Показать исходный код

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

Post.where(published: true).load # => #<ActiveRecord::Relation>
# File activerecord/lib/active_record/relation.rb, line 491
def load
  exec_queries unless loaded?

  self
end
many?() { |*block_args| ... } Показать исходный код

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

# File activerecord/lib/active_record/relation.rb, line 272
def many?
  if block_given?
    to_a.many? { |*block_args| yield(*block_args) }
  else
    limit_value ? to_a.many? : size > 1
  end
end
new(*args, &block) Показать исходный код

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

Ожидает аргументов в том же формате, что и 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
# File activerecord/lib/active_record/relation.rb, line 114
def new(*args, &block)
  scoping { @klass.new(*args, &block) }
end
Также алиасируется как: build
pretty_print(q) Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 587
def pretty_print(q)
  q.pp(self.to_a)
end
reload() Показать исходный код

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

# File activerecord/lib/active_record/relation.rb, line 498
def reload
  reset
  load
end
reset() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 503
def reset
  @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
  @should_eager_load = @join_dependency = nil
  @records = []
  @offsets = {}
  self
end
scope_for_create() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 550
def scope_for_create
  @scope_for_create ||= where_values_hash.merge(create_with_value)
end
scoping() { || ... } Показать исходный код

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

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) во время выполнения блока.

# File activerecord/lib/active_record/relation.rb, line 289
def scoping
  previous, klass.current_scope = klass.current_scope, self
  yield
ensure
  klass.current_scope = previous
end
size() Показать исходный код

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

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

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

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

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

User.where(name: 'Oscar').to_sql
# => SELECT "users".* FROM "users"  WHERE "users"."name" = 'Oscar'
# File activerecord/lib/active_record/relation.rb, line 515
def to_sql
  @to_sql ||= begin
                relation   = self
                connection = klass.connection
                visitor    = connection.visitor

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

                ast   = relation.arel.ast
                binds = relation.bind_values.dup
                visitor.accept(ast) do
                  connection.quote(*binds.shift.reverse)
                end
              end
end
uniq_value() Показать исходный код

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

# File activerecord/lib/active_record/relation.rb, line 571
def uniq_value
  distinct_value
end
update(id, attributes) Показать исходный код

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

Параметры

  • 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)
# File activerecord/lib/active_record/relation.rb, line 351
def update(id, attributes)
  if id.is_a?(Array)
    id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
  else
    object = find(id)
    object.update(attributes)
    object
  end
end
update_all(updates) Показать исходный код

Обновляет все записи с подробными данными, если они соответствуют набору условий, также могут быть заданы лимиты и порядок. Этот метод создает одно SQL-утверждение UPDATE и отправляет его непосредственно в базу данных. Он не создает экземпляры вовлеченных моделей и не вызывает обратные вызовы или проверки 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')
# File activerecord/lib/active_record/relation.rb, line 315
def update_all(updates)
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?

  stmt = Arel::UpdateManager.new(arel.engine)

  stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
  stmt.table(table)
  stmt.key = table[primary_key]

  if joins_values.any?
    @klass.connection.join_to_update(stmt, arel)
  else
    stmt.take(arel.limit)
    stmt.order(*arel.orders)
    stmt.wheres = arel.constraints
  end

  @klass.connection.update stmt, 'SQL', bind_values
end
values() Показать исходный код
# File activerecord/lib/active_record/relation.rb, line 596
def values
  Hash[@values]
end
where_values_hash(relation_table_name = table_name) Показать исходный код

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

User.where(name: 'Oscar').where_values_hash
# => {name: "Oscar"}
# File activerecord/lib/active_record/relation.rb, line 537
def where_values_hash(relation_table_name = table_name)
  equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
    node.left.relation.name == relation_table_name
  }

  binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]

  Hash[equalities.map { |where|
    name = where.left.name
    [name, binds.fetch(name.to_s) { where.right }]
  }]
end

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

Spec-Zone.ru

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