如何重写此行以避免“#<ActiveRecord::Associations::CollectionProxy []”错误?

How do I rewrite this line to avoid the "#<ActiveRecord::Associations::CollectionProxy []" error?

我有这些模型...

class Administrator < ApplicationRecord
    ...
    has_many :locations


class Location < ApplicationRecord
    ...
    has_many :displays, :dependent => :destroy

我有这段代码,用于检索符合特定条件的所有显示...

  @displays = []
  current_user.locations.each do |location|
    @displays = (@displays + location.displays.where(:user => user).includes(:administrator)).uniq
  end

我想找到一种巧妙的 Rails 方法将上面的内容简化为一行,所以我尝试了这个

  @displays = current_user.locations.displays.where(:user => user).includes(:administrator).flatten.uniq

但这会导致错误

undefined method `displays' for #<ActiveRecord::Associations::CollectionProxy []>

有没有办法在一行中重写我的初始块?

您在 locations 而不是 location 呼叫 displays

您可能正在寻找类似的东西:

current_user.locations.joins(:displays).where(displays: { user: user }) ...

您可以将 class 方法添加到您的 Location 模型以获得您想要的结果。

class Location < ApplicationRecord
  has_many :displays, dependent: :destroy

  def self.displays
    Display.where(location_id: select(:id))
  end
end

哪个应该允许您使用:

@displays = current_user.locations.displays.where(user: user).includes(:administrator)

如果您不想向您的模型添加助手,您可以改变您的记录获取技术。

location_ids = current_user.locations.pluck(:id)
@displays = Display.where(location_id: location_ids, user: user).includes(:administrator)

对于单行,您可以简单地将以上几行合并在一起。但是我会选择多行解决方案,因为这条线会变得很长。

@displays = Display.where(location_id: current_user.locations.pluck(:id), user: user).includes(:administrator)

在这两种情况下,都不需要 flatten 或对结果调用 uniq

您可能希望将 select(:id) 换成 pluck(:id),反之亦然。不同之处在于 select 将创建一个子查询。虽然 pluck 首先执行仅获取位置 ID 的查询,然后使用它们创建新查询。这确实会产生一个额外的查询,但可能会更快,因为查询不那么复杂。

您还可以使用以下范围代替 class 方法:

scope :displays, -> { Display.where(location_id: select(:id)) }

我使用 class 方法的原因是 definition/consistency。让我引用 GNU 版国际英语协作词典中的 definition of "scope"

noun That at which one aims; the thing or end to which the mind directs its view; that which is purposed to be reached or accomplished; hence, ultimate design, aim, or purpose; intention; drift; object.

从上面我们可以得出结论,范围应该在当前范围上添加限制以缩小结果的目标。因此,范围应该只有 return 当前范围的限制版本。由于 displays return 是一个全新的范围,具有不同的结果记录(Display 的实例而不是 Location)我选择了 class 方法. (虽然用法相同。)