我有这些型号...
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块?
您是在displays
而不是locations
上呼叫location
。
您可能正在寻找类似的东西:
current_user.locations.joins(:displays).where(displays: { user: user }) ...
您可以将类方法添加到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
的实例),所以我选择了一个类方法。 (尽管用法是相同的。)