如何重写此行,以避免出现“#

问题描述 投票:0回答:2

我有这些型号...

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

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

ruby model ruby-on-rails-5 rails-activerecord entity-relationship
2个回答
0
投票

您是在displays而不是locations上呼叫location

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

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


0
投票

您可以将类方法添加到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的实例),所以我选择了一个类方法。 (尽管用法是相同的。)

© www.soinside.com 2019 - 2024. All rights reserved.