跳至内容 跳至搜索

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 直接计算的,并且本身不会触发实际帖子记录的实例化。

方法
#
A
B
C
D
E
F
I
L
M
N
P
R
S
T

实例公共方法

<<(*records)

通过将它们的外部键设置为关联的主键,将一个或多个 records 添加到集合中。由于 << 将其参数列表展平并插入每个记录,因此 pushconcat 的行为相同。返回 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>
#    ]
也称为:pushappendconcat
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 1049
def <<(*records)
  proxy_association.concat(records) && self
end

==(other)

等效于 Array#==。如果两个数组包含相同数量的元素,并且每个元素都等于 other 数组中的对应元素,则返回 true,否则返回 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 980
def ==(other)
  load_target == other
end

any?()

如果集合不为空,则返回 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 836
      

append(*records)

别名:<<

build(attributes = {}, &block)

返回一个集合类型的新对象,该对象已使用 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
也称为:new
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 318
def build(attributes = {}, &block)
  @association.build(attributes, &block)
end

calculate(operation, column_name)

# 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

clear()

等效于 delete_all。不同之处在于返回 self,而不是包含已删除对象的数组,因此可以将方法链接在一起。有关更多信息,请参见 delete_all。请注意,由于 delete_all 通过直接将 SQL 查询运行到数据库中来删除记录,因此对象的 updated_at 列不会更改。

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 1066
def clear
  delete_all
  self
end

concat(*records)

别名:<<

count(column_name = nil, &block)

计算所有记录。

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 733
      

create(attributes = {}, &block)

返回一个集合类型的新对象,该对象已使用属性实例化,链接到此对象,并且已经保存(如果它通过了验证)。

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 349
def create(attributes = {}, &block)
  @association.create(attributes, &block)
end

create!(attributes = {}, &block)

类似于 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 365
def create!(attributes = {}, &block)
  @association.create!(attributes, &block)
end

delete(*records)

根据 :dependent 选项指定的策略,从集合中删除提供的 records。如果没有给出 :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,则通过调用其 destroy 方法删除所有 records。有关更多信息,请参见 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

您可以传递 IntegerString 值,它会查找响应 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 620
def delete(*records)
  @association.delete(*records).tap { reset_scope }
end

delete_all(dependent = nil)

根据 :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>
#    ]

如果 :dependent 选项设置为 :destroyhas_manyhas_many :through 依赖项默认都使用 :delete_all 策略。不会实例化记录,也不会触发回调。

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 474
def delete_all(dependent = nil)
  @association.delete_all(dependent).tap { reset_scope }
end

destroy(*records)

销毁提供的 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)

您可以传递 IntegerString 值,它会查找响应 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 692
def destroy(*records)
  @association.destroy(*records).tap { reset_scope }
end

destroy_all()

直接从数据库中删除集合的记录,而忽略 :dependent 选项。会实例化记录,并调用 before_removeafter_removebefore_destroyafter_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 501
def destroy_all
  @association.destroy_all.tap { reset_scope }
end

distinct(value = true)

指定记录应该是唯一的还是不唯一的。

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 697
      

empty?()

如果集合为空,则返回 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 831
def empty?
  @association.empty?
end

fifth()

first 相同,但只返回第五条记录。

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 201
      

find(*args)

在集合中查找响应 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 138
def find(*args)
  return super if block_given?
  @association.find(*args)
end

first(limit = nil)

返回集合中的第一条记录,或前 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 144
      

forty_two()

first 相同,但只返回第 42 条记录。 也被称为访问“the reddit”。

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 209
      

fourth()

first 相同,但只返回第四条记录。

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 193
      

include?(record)

如果给定的 record 出现在集合中,则返回 true

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 927
def include?(record)
  !!@association.include?(record)
end

last(limit = nil)

返回集合中的最后一条记录,或最后 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 259
def last(limit = nil)
  load_target if find_from_target?
  super
end

length()

通过对目标调用 size 来返回集合的大小。 如果集合已加载,则 lengthsize 相等。 如果没有,并且您将需要记录,则此方法将少执行一次查询。 否则,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 787
      

load_target()

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 44
def load_target
  @association.load_target
end

loaded()

别名:loaded?

loaded?()

如果关联已加载,则返回 true,否则返回 false

person.pets.loaded? # => false
person.pets.records
person.pets.loaded? # => true
也被称为:loaded
# File activerecord/lib/active_record/associations/collection_proxy.rb, line 53
def loaded?
  @association.loaded?
end

many?()

如果集合包含超过一条记录,则返回 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 877
      

new(attributes = {}, &block)

别名:build

pluck(*column_names)

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 728
def pluck(*column_names)
  null_scope? ? scope.pluck(*column_names) : super
end

proxy_association()

返回集合的关联对象。

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 944
def proxy_association
  @association
end

push(*records)

别名:<<

reload()

从数据库中重新加载集合。 返回 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 1085
def reload
  proxy_association.reload(true)
  reset_scope
end

replace(other_array)

other_array 替换此集合。 这将执行一个 diff 操作,并且只删除/添加已更改的记录。

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 391
def replace(other_array)
  @association.replace(other_array)
end

reset()

卸载关联。 返回 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 1106
def reset
  proxy_association.reset
  proxy_association.reset_scope
  reset_scope
end

scope()

返回此关联中记录的 Relation 对象。

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 949
def scope
  @scope ||= @association.scope
end

second()

first 相同,但只返回第二条记录。

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 177
      

second_to_last()

last 相同,但只返回倒数第二条记录。

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 226
      

select(*fields, &block)

有两种工作方式。

第一种: 指定要从结果集中选择的字段子集。

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

第二种: 您可以传递一个 block,以便它可以像 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 59
      

size()

返回集合的大小。 如果集合尚未加载,它将执行一个 SELECT COUNT(*) 查询。 否则,它将调用 collection.size

如果集合已加载,则 sizelength 相等。 如果没有,并且您将需要记录,则 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 782
def size
  @association.size
end

take(limit = nil)

从集合中获取一条记录(如果提供参数,则获取 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 289
def take(limit = nil)
  load_target if find_from_target?
  super
end

target()

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 40
def target
  @association.target
end

third()

first 相同,但只返回第三条记录。

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 185
      

third_to_last()

last 相同,只是返回倒数第三个记录。

# File activerecord/lib/active_record/associations/collection_proxy.rb, line 218