класс ActiveRecord::Relation
Active Record Связь
Константы
- INVALID_METHODS_FOR_DELETE_ALL
- MULTI_VALUE_METHODS
- SINGLE_VALUE_METHODS
- VALUE_METHODS
Атрибуты
Общедоступные методы класса
# File activerecord/lib/active_record/relation.rb, line 22
def initialize(klass, table, values = {})
@klass = klass
@table = table
@values = values
@offsets = {}
@loaded = false
end Общедоступные методы экземпляров
# File activerecord/lib/active_record/relation.rb, line 604
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 Сравнивает два отношения на предмет равенства.
# File activerecord/lib/active_record/relation.rb, line 273
def any?
if block_given?
to_a.any? { |*block_args| yield(*block_args) }
else
!empty?
end
end Возвращает true, если существуют какие-либо записи.
# File activerecord/lib/active_record/relation.rb, line 620 def blank? to_a.blank? end
Возвращает true, если отношение пустое.
# File activerecord/lib/active_record/relation.rb, line 141
def create(*args, &block)
scoping { @klass.create(*args, &block) }
end Попытка создать новую запись с теми же атрибутами, что и в отношении. Возвращает инициализированный объект, если проверка данных завершилась неудачей.
Ожидаются аргументы в том же формате, что и 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 149
def create!(*args, &block)
scoping { @klass.create!(*args, &block) }
end Аналогично create, но вызывает create! в базовом классе. Выбрасывает исключение, если произошла ошибка проверки данных.
Ожидаются аргументы в том же формате, что и Base.create!.
# File activerecord/lib/active_record/relation.rb, line 503 def delete(id_or_array) where(primary_key => id_or_array).delete_all end
Удаляет строку с первичным ключом, соответствующим аргументу id, используя SQL-запрос DELETE, и возвращает количество удалённых строк. Объекты Active Record не создаются, поэтому обратные вызовы объекта не выполняются, в том числе любые параметры ассоциации :dependent.
Вы можете удалить несколько строк одновременно, передав массив id.
Примечание: Хотя это часто намного быстрее, чем альтернатива, пропуск обратных вызовов может обойти бизнес-логику в вашем приложении, которая гарантирует целостность ссылок или выполняет другие важные задачи.
Примеры
# Delete a single row Todo.delete(1) # Delete multiple rows Todo.delete([2,3,4])
# File activerecord/lib/active_record/relation.rb, line 452
def delete_all(conditions = nil)
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
if MULTI_VALUE_METHODS.include?(method)
send("#{method}_values").any?
else
send("#{method}_value")
end
}
if invalid_methods.any?
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
end
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
bvs = arel.bind_values + bind_values
affected = @klass.connection.delete(stmt, 'SQL', bvs)
reset
affected
end
end Удаляет записи, соответствующие 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
# File activerecord/lib/active_record/relation.rb, line 222 def find_or_initialize_by(attributes, &block) find_by(attributes) || new(attributes, &block) end
Как find_or_create_by, но вызывает new вместо create.
# 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
# File activerecord/lib/active_record/relation.rb, line 628
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 # File activerecord/lib/active_record/relation.rb, line 593 def joined_includes_values includes_values & joins_values end
Соединения, которые также помечены для предварительной загрузки. В этом случае мы должны просто загрузить их жадно. Обратите внимание, что это наивное реализация, так как у нас могут быть строки и символы, которые представляют одну и ту же ассоциацию, но которые не совпадают с этим. Кроме того, у нас могут быть вложенные хэши, которые частично совпадают, например { a: :b } и { a: [:b, :c] }
# File activerecord/lib/active_record/relation.rb, line 513 def load exec_queries unless loaded? self end
Загружает записи из базы данных, если они еще не загружены. Вы можете использовать это, если по какой-то причине вам нужно явно загрузить некоторые записи, прежде чем фактически использовать их. Возвращаемое значение — сам объект отношения, а не записи.
Post.where(published: true).load # => #<ActiveRecord::Relation>
# File activerecord/lib/active_record/relation.rb, line 282
def many?
if block_given?
to_a.many? { |*block_args| yield(*block_args) }
else
limit_value ? to_a.many? : size > 1
end
end Возвращает true, если записей более одной.
# File activerecord/lib/active_record/relation.rb, line 118
def new(*args, &block)
scoping { @klass.new(*args, &block) }
end Инициализирует новую запись из отношения, сохраняя текущий контекст.
Ожидает аргументы в том же формате, что и 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 615 def pretty_print(q) q.pp(self.to_a) end
# File activerecord/lib/active_record/relation.rb, line 520 def reload reset load end
Вынуждает перезагрузку отношения.
# File activerecord/lib/active_record/relation.rb, line 525
def reset
@last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
@should_eager_load = @join_dependency = nil
@records = []
@offsets = {}
self
end # File activerecord/lib/active_record/relation.rb, line 578 def scope_for_create @scope_for_create ||= where_values_hash.merge(create_with_value) end
# File activerecord/lib/active_record/relation.rb, line 299 def scoping previous, klass.current_scope = klass.current_scope, 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) во время выполнения блока.
# File activerecord/lib/active_record/relation.rb, line 256 def size loaded? ? @records.length : count(:all) end
Возвращает размер записей.
# File activerecord/lib/active_record/relation.rb, line 241 def to_a load @records end
Преобразует объекты отношения в Массив.
# File activerecord/lib/active_record/relation.rb, line 537
def to_sql
@to_sql ||= begin
relation = self
connection = klass.connection
visitor = connection.visitor
if eager_loading?
find_with_associations { |rel| relation = rel }
end
arel = relation.arel
binds = (arel.bind_values + relation.bind_values).dup
binds.map! { |bv| connection.quote(*bv.reverse) }
collect = visitor.accept(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'
# File activerecord/lib/active_record/relation.rb, line 599 def uniq_value distinct_value end
uniq и uniq! устарели. uniq_value делегирует distinct_value, чтобы сохранить обратную совместимость. Используйте distinct_value вместо этого.
# File activerecord/lib/active_record/relation.rb, line 363
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 Обновляет объект (или несколько объектов) и сохраняет его в базе данных, если валидация пройдена. Результирующий объект возвращается, независимо от того, был ли объект успешно сохранен в базе данных или нет.
Параметры
-
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 326
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
bvs = arel.bind_values + bind_values
@klass.connection.update stmt, 'SQL', bvs
end Обновляет все записи в текущем отношении с указанными данными. Этот метод строит одно SQL-выражение UPDATE и отправляет его напрямую в базу данных. Он не создает связанные модели и не запускает Active Record обратные вызовы или валидацию. Значения, передаваемые в `update_all`, не будут проходить типизацию Active Record. Оно должно принимать только значения, которые могут быть переданы непосредственно в SQL-базу данных.
Параметры
-
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 624 def values Hash[@values] end
# File activerecord/lib/active_record/relation.rb, line 559
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) {
case where.right
when Array then where.right.map(&:val)
when Arel::Nodes::Casted
where.right.val
end
}]
}]
end Возвращает хэш условий where.
User.where(name: 'Oscar').where_values_hash
# => {name: "Oscar"}
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.