如何在Rails控制器中调用模型装饰器的purchase方法筛选商品
解决Rails控制器中筛选高销量活跃商品的问题
嘿,我来帮你搞定这个问题!你的思路是对的,但当前的写法没法直接在ActiveRecord查询链里调用decorator的方法——原因很简单,purchase是你写在decorator里的方法,它返回的是一个「商品ID => 销量」的哈希,而不是ActiveRecord能识别的查询对象,所以没法直接跟在where后面链式调用。
下面给你两种可行的解决方案,优先推荐第一种(效率更高):
方案一:用数据库层面的JOIN + GROUP BY筛选(推荐)
直接在ActiveRecord查询链里完成所有筛选逻辑,让数据库帮你做计算,效率会比先查所有数据再处理高很多:
def index one_year_ago = Date.today - 365.days @products = Product.where(active: true) # 关联line_items和对应的order表 .joins(line_items: :order) # 筛选过去一年完成的订单 .where(orders: { state: 'complete', completed_at: one_year_ago..Date.today }) # 按商品ID分组 .group('products.id') # 筛选销量总和大于1的商品 .having('SUM(line_items.quantity) > 1') end
这个写法的逻辑是:
- 先筛选出所有活跃商品
- 关联这些商品的
line_items(订单项)和对应的order(订单) - 只保留状态为
complete且完成时间在过去一年的订单 - 按商品ID分组,计算每个商品的总销量
- 最后只保留总销量大于1的商品
方案二:复用decorator的方法(适合小数据量场景)
如果你一定要复用product_decorator.rb里的purchase方法,可以先获取销量哈希,再提取符合条件的商品ID,最后查询商品:
def index one_year_ago = Date.today - 365.days # 调用decorator的purchase方法获取销量哈希 sales_hash = ProductDecorator.new(Product).purchase(one_year_ago, Date.today) # 筛选出销量大于1的商品ID qualified_product_ids = sales_hash.select { |product_id, quantity| quantity > 1 }.keys # 查询活跃且符合销量条件的商品 @products = Product.where(active: true, id: qualified_product_ids) end
不过这个方法有个缺点:会触发两次数据库查询(一次查销量,一次查商品),数据量大的时候效率不如方案一。另外补充一句:decorator的设计初衷一般是用来处理模型的展示逻辑(比如格式化输出),这种数据筛选逻辑其实更适合放在模型的scope里,或者直接写在控制器的查询链中。
内容的提问来源于stack exchange,提问作者Grant Leslie
相关产品推荐
相关产品推荐

