【问题标题】:How do I rewrite this line to avoid the "#<ActiveRecord::Associations::CollectionProxy []" error?如何重写此行以避免“#<ActiveRecord::Associations::CollectionProxy []”错误?
【发布时间】:2020-03-15 12:31:48
【问题描述】:

我有这些模型...

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 []>

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

【问题讨论】:

    标签: ruby model ruby-on-rails-5 rails-activerecord entity-relationship


    【解决方案1】:

    您可以在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 的查询,然后使用它们来创建一个新查询。这确实会产生额外的查询,但可能会更快,因为查询不太复杂。

    您也可以使用以下范围代替类方法:

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

    我使用类方法的原因是出于定义/一致性的原因。让我引用 GNU 版本的国际协作英语词典中的definition of "scope"

    名词头脑引导其观点的事物或目的;旨在达到或完成的目标;因此,最终的设计、目标或目的;意图;漂移;对象。

    从上面我们可以得出结论,作用域应该对当前作用域增加限制,以缩小结果的目标。因此,范围应该只返回当前范围的受限版本。由于displays 返回一个全新的范围,并具有不同的结果记录(Display 的实例而不是Location),因此我选择了类方法。 (虽然用法是一样的。)

    【讨论】:

    • 感谢您的分析。最终,我采用了您的单行解决方案“Display.where(location_id: current_user.locations.pluck(:id), user: user).includes(:administrator)”。将其中一些放在不同的行上可以使格式很好。
    【解决方案2】:

    你在locations而不是location上调用displays

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

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

    【讨论】:

    • 谢谢,但您能填写其余的“...”吗?该语句应返回“显示”,但正如您所拥有的那样,它返回“位置”。
    • 不,他们不能。用户与任何其他实体之间没有关系,它纯粹只是一个 where 子句参数。管理员 has_many 位置,其中 has_many 显示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多