我可以在不实例化相关模型的情况下从一对一关系中急切加载属性以进行委托吗?
Can I eager load attributes from one-to-one relationship for delegation without instantiating the related model?
想象一下:
class House < ActiveRecord::Base
belongs_to :ground
delegate :elevation_in_meters, to: :ground
# attributes: stories, roof_type
end
class Ground < ActiveRecord::Base
has_one :house
# attributes: elevation_in_meters, geo_data
end
然后到 eager load ground 以便 house.elevation_in_meters
可以在不加载的情况下被调用 Ground
我可以这样做:
houses=House.includes(:ground).first(3)
这个问题是,整个 Ground
对象实际上是用所有属性实例化的,包括 geo_data
属性——在这种情况下我不需要它。我关心的原因是,查询需要非常高效,并且 geo_data
是一个非常大的文本字段。我只需要读取委托属性,而不是写入它们。
我可以采用什么方法来预先加载 Ground
中的 elevation_in_meters
属性而不加载 Ground
中的所有内容?
我在 rails 4.1 顺便说一句
注意:我希望 House
默认具有这种预加载行为,这样我就不需要每次都指定它。
首先为您想要部分获取的模型写一个范围,select您喜欢的字段。请注意,我使用了全名(table 名称)和 select 的字符串。我不确定您是否可以 select(:elevation_in_meters,:geo_data)
因为我已经从我们的生产示例中复制了它,并且我们使用了一些与此范围的连接,如果没有 table 名称将无法工作。自己试试吧。
class Ground < ActiveRecord::Base
has_one :house
attributes: elevation_in_meters, geo_data
scope :reduced, -> {
select('grounds.elevation_in_meters, grounds.geo_data')
}
end
有了作用域,您可以创建第二个 belongs_to 关系(不要害怕它会弄乱您的第一个关系,因为 rails 关系基本上只是为您创建的方法),调用 Ground
模型上的范围。
class House < ActiveRecord::Base
belongs_to :ground
belongs_to :ground_reduced, ->(_o) { reduced },
class_name: 'Ground', foreign_key: 'ground_id'
delegate :elevation_in_meters, to: :ground_reduced
# go for an additional delegation if
# you also need this with the full object sometimes
end
最后你可以像这样调用你的查询:
houses = House.includes(:ground_reduced).first(3)
从技术上讲,这不是您问题的正确答案,因为 Ground
对象仍处于实例化状态。但是该实例只会包含您想要的数据,而其他字段将为零,因此它应该可以解决问题。
更新:
正如我刚刚看到的那样,您希望最好将此行为作为默认行为,只需为您的房屋添加一个范围:
scope :reduced, -> { includes(:ground_reduced) }
然后您可以将其添加为您的默认范围,因为您的原始关系不会受此影响。
我知道已经有一段时间了,但我偶然发现了这个。
如果您只对单个属性感兴趣,您也可以将 joins
与 select
结合使用,该属性将神奇地添加到您的 House 实例中。
res = House.joins(:ground).select('houses.*, grounds.elevation_in_meters').first
res.elevation_in_meters # attribute is available on the object
要始终显示此属性,请将其设置为 House
的 default_scope
,如下所示:
default_scope { joins(:ground).select('houses.*, grounds.elevation_in_meters') }
根据您要加入的表格的性质,您可能还需要 distinct
。
想象一下:
class House < ActiveRecord::Base
belongs_to :ground
delegate :elevation_in_meters, to: :ground
# attributes: stories, roof_type
end
class Ground < ActiveRecord::Base
has_one :house
# attributes: elevation_in_meters, geo_data
end
然后到 eager load ground 以便 house.elevation_in_meters
可以在不加载的情况下被调用 Ground
我可以这样做:
houses=House.includes(:ground).first(3)
这个问题是,整个 Ground
对象实际上是用所有属性实例化的,包括 geo_data
属性——在这种情况下我不需要它。我关心的原因是,查询需要非常高效,并且 geo_data
是一个非常大的文本字段。我只需要读取委托属性,而不是写入它们。
我可以采用什么方法来预先加载 Ground
中的 elevation_in_meters
属性而不加载 Ground
中的所有内容?
我在 rails 4.1 顺便说一句
注意:我希望 House
默认具有这种预加载行为,这样我就不需要每次都指定它。
首先为您想要部分获取的模型写一个范围,select您喜欢的字段。请注意,我使用了全名(table 名称)和 select 的字符串。我不确定您是否可以 select(:elevation_in_meters,:geo_data)
因为我已经从我们的生产示例中复制了它,并且我们使用了一些与此范围的连接,如果没有 table 名称将无法工作。自己试试吧。
class Ground < ActiveRecord::Base
has_one :house
attributes: elevation_in_meters, geo_data
scope :reduced, -> {
select('grounds.elevation_in_meters, grounds.geo_data')
}
end
有了作用域,您可以创建第二个 belongs_to 关系(不要害怕它会弄乱您的第一个关系,因为 rails 关系基本上只是为您创建的方法),调用 Ground
模型上的范围。
class House < ActiveRecord::Base
belongs_to :ground
belongs_to :ground_reduced, ->(_o) { reduced },
class_name: 'Ground', foreign_key: 'ground_id'
delegate :elevation_in_meters, to: :ground_reduced
# go for an additional delegation if
# you also need this with the full object sometimes
end
最后你可以像这样调用你的查询:
houses = House.includes(:ground_reduced).first(3)
从技术上讲,这不是您问题的正确答案,因为 Ground
对象仍处于实例化状态。但是该实例只会包含您想要的数据,而其他字段将为零,因此它应该可以解决问题。
更新: 正如我刚刚看到的那样,您希望最好将此行为作为默认行为,只需为您的房屋添加一个范围:
scope :reduced, -> { includes(:ground_reduced) }
然后您可以将其添加为您的默认范围,因为您的原始关系不会受此影响。
我知道已经有一段时间了,但我偶然发现了这个。
如果您只对单个属性感兴趣,您也可以将 joins
与 select
结合使用,该属性将神奇地添加到您的 House 实例中。
res = House.joins(:ground).select('houses.*, grounds.elevation_in_meters').first
res.elevation_in_meters # attribute is available on the object
要始终显示此属性,请将其设置为 House
的 default_scope
,如下所示:
default_scope { joins(:ground).select('houses.*, grounds.elevation_in_meters') }
根据您要加入的表格的性质,您可能还需要 distinct
。