класс ActiveRecord::Associations::CollectionProxy
Прокси коллекции Active Record
Прокси коллекций в Active Record выступают посредниками между association и его набором результатов target.
Например, если дано
class Blog < ActiveRecord::Base has_many :posts end blog = Blog.first
Прокси коллекции, возвращаемый blog.posts, создаётся на основе :has_many association и делегирует вызовы коллекции записей, используя в качестве target.
Этот класс делегирует неизвестные методы классу отношения association с помощью кэша делегирования.
Набор результатов target не загружается, пока это не потребуется. Например,
blog.posts.count
вычисляется непосредственно с помощью SQL и сам по себе не приводит к созданию экземпляров фактических записей публикаций.
Общедоступные методы экземпляра
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 1049 def <<(*records) proxy_association.concat(records) && self end
Добавляет одну или несколько записей records в коллекцию, устанавливая для них внешние ключи, равные первичному ключу связи. Поскольку << разворачивает список аргументов и добавляет каждую запись, push и concat работают одинаково. Возвращает self, поэтому можно объединять несколько операций добавления в цепочку.
class Person < ActiveRecord::Base has_many :pets end person.pets.size # => 0 person.pets << Pet.new(name: 'Fancy-Fancy') person.pets << [Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo')] person.pets.size # => 3 person.id # => 1 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ]
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 980 def ==(other) load_target == other end
Эквивалентно Array#==. Возвращает true, если оба массива содержат одинаковое количество элементов и каждый элемент равен соответствующему элементу в массиве other; в противном случае возвращает false.
class Person < ActiveRecord::Base has_many :pets end person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1> # ] other = person.pets.to_ary person.pets == other # => true
Обратите внимание, что несохранённые записи всё равно могут считаться равными:
other = [Pet.new(id: 1), Pet.new(id: 2)] person.pets == other # => true
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 836
Возвращает true, если коллекция не пуста.
class Person < ActiveRecord::Base has_many :pets end person.pets.count # => 0 person.pets.any? # => false person.pets << Pet.new(name: 'Snoop') person.pets.count # => 1 person.pets.any? # => true
Вызов без блока, если коллекция ещё не загружена, эквивалентен collection.exists?. Если вы в любом случае собираетесь загрузить коллекцию, лучше вызвать collection.load.any?, чтобы избежать лишнего запроса.
Также можно передать block для определения критериев. Поведение остаётся тем же: метод возвращает true, если коллекция, соответствующая критериям, не пуста.
person.pets # => [#<Pet name: "Snoop", group: "dogs">] person.pets.any? do |pet| pet.group == 'cats' end # => false person.pets.any? do |pet| pet.group == 'dogs' end # => true
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 318
def build(attributes = {}, &block)
@association.build(attributes, &block)
end Возвращает новый объект типа коллекции, созданный с attributes и связанный с этим объектом, но ещё не сохранённый. Можно передать массив хешей атрибутов; в этом случае будет возвращён массив новых объектов.
class Person
has_many :pets
end
person.pets.build
# => #<Pet id: nil, name: nil, person_id: 1>
person.pets.build(name: 'Fancy-Fancy')
# => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>
person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
# => [
# #<Pet id: nil, name: "Spook", person_id: 1>,
# #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
# #<Pet id: nil, name: "Brain", person_id: 1>
# ]
person.pets.size # => 5 # size of the collection
person.pets.count # => 0 # count from database
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 724 def calculate(operation, column_name) null_scope? ? scope.calculate(operation, column_name) : super end
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 1066 def clear delete_all self end
Эквивалентно delete_all. Отличие в том, что метод возвращает self, а не массив удалённых объектов, поэтому методы можно объединять в цепочку. Дополнительные сведения см. в разделе delete_all. Обратите внимание: поскольку delete_all удаляет записи, напрямую выполняя SQL-запрос к базе данных, столбец updated_at объекта не изменяется.
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 733
Подсчитывает все записи.
class Person < ActiveRecord::Base has_many :pets end # This will perform the count using SQL. person.pets.count # => 3 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ]
Если передать блок, все записи питомцев человека будут выбраны в SQL, а затем подсчитаны с помощью Ruby.
person.pets.count { |pet| pet.name.include?('-') } # => 2
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 349
def create(attributes = {}, &block)
@association.create(attributes, &block)
end Возвращает новый объект типа коллекции, созданный с атрибутами, связанный с этим объектом и уже сохранённый (если он прошёл проверки валидации).
class Person
has_many :pets
end
person.pets.create(name: 'Fancy-Fancy')
# => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}])
# => [
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.size # => 3
person.pets.count # => 3
person.pets.find(1, 2, 3)
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 365
def create!(attributes = {}, &block)
@association.create!(attributes, &block)
end Как create, но вызывает исключение, если запись недопустима.
class Person has_many :pets end class Pet validates :name, presence: true end person.pets.create!(name: nil) # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 620
def delete(*records)
@association.delete(*records).tap { reset_scope }
end Удаляет переданные records из коллекции в соответствии со стратегией, заданной параметром :dependent. Если параметр :dependent не указан, используется стратегия по умолчанию. Возвращает массив удалённых записей.
Для связей has_many :through стратегией удаления по умолчанию является :delete_all.
Для связей has_many стратегией удаления по умолчанию является :nullify. При этом внешние ключи устанавливаются в NULL.
class Person < ActiveRecord::Base has_many :pets # dependent: :nullify option by default end person.pets.size # => 3 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.delete(Pet.find(1)) # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>] person.pets.size # => 2 person.pets # => [ # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] Pet.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
Если задано значение :destroy, все records удаляются вызовом их метода destroy. Дополнительные сведения см. в разделе destroy.
class Person < ActiveRecord::Base has_many :pets, dependent: :destroy end person.pets.size # => 3 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.delete(Pet.find(1), Pet.find(3)) # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.size # => 1 person.pets # => [#<Pet id: 2, name: "Spook", person_id: 1>] Pet.find(1, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
Если задано значение :delete_all, все records удаляются без вызова их метода destroy.
class Person < ActiveRecord::Base has_many :pets, dependent: :delete_all end person.pets.size # => 3 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.delete(Pet.find(1)) # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>] person.pets.size # => 2 person.pets # => [ # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] Pet.find(1) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
Можно передать значения Integer или String; метод находит записи, соответствующие id, и выполняет для них удаление.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.delete("1")
# => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
person.pets.delete(2, 3)
# => [
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 474
def delete_all(dependent = nil)
@association.delete_all(dependent).tap { reset_scope }
end Удаляет все записи из коллекции в соответствии со стратегией, заданной параметром :dependent. Если параметр :dependent не указан, используется стратегия по умолчанию.
Для связей has_many :through стратегией удаления по умолчанию является :delete_all.
Для связей has_many стратегией удаления по умолчанию является :nullify. При этом внешние ключи устанавливаются в NULL.
class Person < ActiveRecord::Base has_many :pets # dependent: :nullify option by default end person.pets.size # => 3 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.delete_all # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.size # => 0 person.pets # => [] Pet.find(1, 2, 3) # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>, # #<Pet id: 2, name: "Spook", person_id: nil>, # #<Pet id: 3, name: "Choo-Choo", person_id: nil> # ]
Для зависимостей has_many и has_many :through по умолчанию используется стратегия :delete_all, если параметру :dependent присвоено значение :destroy. Записи не создаются и обратные вызовы не выполняются.
class Person < ActiveRecord::Base has_many :pets, dependent: :destroy end person.pets.size # => 3 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.delete_all Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
Если задано значение :delete_all, все объекты удаляются без вызова их метода destroy.
class Person < ActiveRecord::Base has_many :pets, dependent: :delete_all end person.pets.size # => 3 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.delete_all Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 692
def destroy(*records)
@association.destroy(*records).tap { reset_scope }
end Удаляет переданные records и исключает их из коллекции. Этот метод всегда удаляет записи из базы данных, игнорируя параметр :dependent. Возвращает массив удалённых записей.
class Person < ActiveRecord::Base has_many :pets end person.pets.size # => 3 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.destroy(Pet.find(1)) # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>] person.pets.size # => 2 person.pets # => [ # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.destroy(Pet.find(2), Pet.find(3)) # => [ # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.size # => 0 person.pets # => [] Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
Можно передать значения Integer или String; метод находит записи, соответствующие id, и удаляет их из базы данных.
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 4, name: "Benny", person_id: 1>,
# #<Pet id: 5, name: "Brain", person_id: 1>,
# #<Pet id: 6, name: "Boss", person_id: 1>
# ]
person.pets.destroy("4")
# => #<Pet id: 4, name: "Benny", person_id: 1>
person.pets.size # => 2
person.pets
# => [
# #<Pet id: 5, name: "Brain", person_id: 1>,
# #<Pet id: 6, name: "Boss", person_id: 1>
# ]
person.pets.destroy(5, 6)
# => [
# #<Pet id: 5, name: "Brain", person_id: 1>,
# #<Pet id: 6, name: "Boss", person_id: 1>
# ]
person.pets.size # => 0
person.pets # => []
Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 501
def destroy_all
@association.destroy_all.tap { reset_scope }
end Удаляет записи коллекции непосредственно из базы данных, игнорируя параметр :dependent. Записи создаются, а метод вызывает обратные вызовы before_remove, after_remove, before_destroy и after_destroy.
class Person < ActiveRecord::Base has_many :pets end person.pets.size # => 3 person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.destroy_all person.pets.size # => 0 person.pets # => [] Pet.find(1) # => Couldn't find Pet with id=1
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 697
Указывает, должны ли записи быть уникальными.
class Person < ActiveRecord::Base has_many :pets end person.pets.select(:name) # => [ # #<Pet name: "Fancy-Fancy">, # #<Pet name: "Fancy-Fancy"> # ] person.pets.select(:name).distinct # => [#<Pet name: "Fancy-Fancy">] person.pets.select(:name).distinct.distinct(false) # => [ # #<Pet name: "Fancy-Fancy">, # #<Pet name: "Fancy-Fancy"> # ]
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 831 def empty? @association.empty? end
Возвращает true, если коллекция пуста. Если коллекция загружена, метод эквивалентен collection.size.zero?. Если коллекция не загружена, он эквивалентен !collection.exists?. Если коллекция ещё не загружена, но вы в любом случае собираетесь получить записи, лучше проверить collection.load.empty?.
class Person < ActiveRecord::Base has_many :pets end person.pets.count # => 1 person.pets.empty? # => false person.pets.delete_all person.pets.count # => 0 person.pets.empty? # => true
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 201
То же, что и first, но возвращает только пятую запись.
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 138 def find(*args) return super if block_given? @association.find(*args) end
Находит объект в коллекции, соответствующий id. Использует те же правила, что и ActiveRecord::FinderMethods.find. Если объект не найден, вызывает ошибку ActiveRecord::RecordNotFound.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=4
person.pets.find(2) { |pet| pet.name.downcase! }
# => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
person.pets.find(2, 3)
# => [
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 144
Возвращает первую запись или первые n записей из коллекции. Если коллекция пуста, первая форма возвращает nil, а вторая — пустой массив.
class Person < ActiveRecord::Base has_many :pets end person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.first # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1> person.pets.first(2) # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1> # ] another_person_without.pets # => [] another_person_without.pets.first # => nil another_person_without.pets.first(3) # => []
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 209
То же, что и first, но возвращает только сорок вторую запись. Также известно как обращение к «реддиту».
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 193
То же, что и first, но возвращает только четвёртую запись.
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 927 def include?(record) !!@association.include?(record) end
Возвращает true, если указанная record присутствует в коллекции.
class Person < ActiveRecord::Base has_many :pets end person.pets # => [#<Pet id: 20, name: "Snoop">] person.pets.include?(Pet.find(20)) # => true person.pets.include?(Pet.find(21)) # => false
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 259 def last(limit = nil) load_target if find_from_target? super end
Возвращает последнюю запись или последние n записей из коллекции. Если коллекция пуста, первая форма возвращает nil, а вторая — пустой массив.
class Person < ActiveRecord::Base has_many :pets end person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.last # => #<Pet id: 3, name: "Choo-Choo", person_id: 1> person.pets.last(2) # => [ # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] another_person_without.pets # => [] another_person_without.pets.last # => nil another_person_without.pets.last(3) # => []
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 787
Возвращает размер коллекции, вызывая size для целевого объекта. Если коллекция уже загружена, length и size эквивалентны. Если она не загружена, но записи в любом случае понадобятся, этот метод позволит выполнить на один запрос меньше. В противном случае size работает эффективнее.
class Person < ActiveRecord::Base has_many :pets end person.pets.length # => 3 # executes something like SELECT "pets".* FROM "pets" WHERE "pets"."person_id" = 1 # Because the collection is loaded, you can # call the collection with no additional queries: person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ]
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 44 def load_target @association.load_target end
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 53 def loaded? @association.loaded? end
Возвращает true, если связь загружена, и false в противном случае.
person.pets.loaded? # => false person.pets.records person.pets.loaded? # => true
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 877
Возвращает true, если коллекция содержит более одной записи. Эквивалентно collection.size > 1.
class Person < ActiveRecord::Base has_many :pets end person.pets.count # => 1 person.pets.many? # => false person.pets << Pet.new(name: 'Snoopy') person.pets.count # => 2 person.pets.many? # => true
Также можно передать block для определения критериев. Поведение остаётся тем же: метод возвращает true, если коллекция, соответствующая критериям, содержит более одной записи.
person.pets # => [ # #<Pet name: "Gorby", group: "cats">, # #<Pet name: "Puff", group: "cats">, # #<Pet name: "Snoop", group: "dogs"> # ] person.pets.many? do |pet| pet.group == 'dogs' end # => false person.pets.many? do |pet| pet.group == 'cats' end # => true
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 728 def pluck(*column_names) null_scope? ? scope.pluck(*column_names) : super end
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 944 def proxy_association @association end
Возвращает объект связи для коллекции.
class Person < ActiveRecord::Base has_many :pets end person.pets.proxy_association # => #<ActiveRecord::Associations::HasManyAssociation owner="#<Person:0x00>">
Возвращает тот же объект, что и person.association(:pets), позволяя выполнять вызовы, например person.pets.proxy_association.owner.
Дополнительные сведения см. в разделе Расширения связей в Associations::ClassMethods.
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 1085 def reload proxy_association.reload(true) reset_scope end
Повторно загружает коллекцию из базы данных. Возвращает self.
class Person < ActiveRecord::Base has_many :pets end person.pets # fetches pets from the database # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>] person.pets # uses the pets cache # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>] person.pets.reload # fetches pets from the database # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 391 def replace(other_array) @association.replace(other_array) end
Заменяет эту коллекцию на other_array. Метод сравнивает коллекции и удаляет или добавляет только изменившиеся записи.
class Person < ActiveRecord::Base has_many :pets end person.pets # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>] other_pets = [Pet.new(name: 'Puff', group: 'celebrities')] person.pets.replace(other_pets) person.pets # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
Если переданный массив содержит записи неправильного типа связи, вызывается ошибка ActiveRecord::AssociationTypeMismatch:
person.pets.replace(["doo", "ggie", "gaga"]) # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 1106 def reset proxy_association.reset proxy_association.reset_scope reset_scope end
Выгружает связь. Возвращает self.
class Person < ActiveRecord::Base has_many :pets end person.pets # fetches pets from the database # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>] person.pets # uses the pets cache # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>] person.pets.reset # clears the pets cache person.pets # fetches pets from the database # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 949 def scope @scope ||= @association.scope end
Возвращает объект Relation для записей этой связи
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 177
То же, что и first, но возвращает только вторую запись.
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 226
То же, что и last, но возвращает только предпоследнюю запись.
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 59
Работает двумя способами.
Первый: укажите подмножество полей, которые нужно выбрать из набора результатов.
class Person < ActiveRecord::Base has_many :pets end person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.select(:name) # => [ # #<Pet id: nil, name: "Fancy-Fancy">, # #<Pet id: nil, name: "Spook">, # #<Pet id: nil, name: "Choo-Choo"> # ] person.pets.select(:id, :name) # => [ # #<Pet id: 1, name: "Fancy-Fancy">, # #<Pet id: 2, name: "Spook">, # #<Pet id: 3, name: "Choo-Choo"> # ]
Будьте осторожны: это также означает, что объект модели будет инициализирован только выбранными полями. При попытке обратиться к полю, отличному от id, которого нет в инициализированной записи, возникнет ошибка:
person.pets.select(:name).first.person_id # => ActiveModel::MissingAttributeError: missing attribute 'person_id' for Pet
Второй: можно передать блок и использовать метод так же, как Array#select. Для области видимости он создаёт массив объектов из базы данных, преобразует результат в массив и перебирает его с помощью Array#select.
person.pets.select { |pet| /oo/.match?(pet.name) }
# => [
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 782 def size @association.size end
Возвращает размер коллекции. Если коллекция не загружена, выполняется запрос SELECT COUNT(*). В противном случае вызывается collection.size.
Если коллекция уже загружена, size и length эквивалентны. Если коллекция не загружена, но записи в любом случае понадобятся, length позволит выполнить на один запрос меньше. В противном случае size работает эффективнее.
class Person < ActiveRecord::Base has_many :pets end person.pets.size # => 3 # executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1 person.pets # This will execute a SELECT * FROM query # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.size # => 3 # Because the collection is already loaded, this will behave like # collection.size and no SQL count query is executed.
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 289 def take(limit = nil) load_target if find_from_target? super end
Возвращает запись (или N записей, если указан параметр) из коллекции, используя те же правила, что и ActiveRecord::FinderMethods.take.
class Person < ActiveRecord::Base has_many :pets end person.pets # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1> person.pets.take(2) # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1> # ] another_person_without.pets # => [] another_person_without.pets.take # => nil another_person_without.pets.take(2) # => []
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 40 def target @association.target end
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 185
То же, что и first, но возвращает только третью запись.
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 218
То же, что и last, но возвращает только третью с конца запись.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.