涉及多个模型的复杂范围

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

[我正在尝试创建一个范围,为我提供在他们获得clients之后就创建了purchase的所有:transitioned_to_maintenance(我们在内部使用的术语)。

我的模型和范围的组织方式如下:

class Client < ApplicationRecord
    has_many :program_transitions
    has_many :purchases

    scope :transitioned_to_maintenance, -> { where(id: ProgramTransition.to_maintenance.pluck(:client_id)) }
    scope :has_purchases, -> { where(id: Purchase.pluck(:client_id)) }
end

class ProgramTransition < ApplicationRecord
  belongs_to :client, required: true

  scope :to_fm, -> { where(new_status: "full maintenance") }
  scope :to_lm, -> { where(new_status: "limited maintenance") }
  scope :to_maintenance, -> { to_fm.or(to_lm)}
end

class Purchase < ActiveRecord::Base
    belongs_to :client
end

我可以通过遍历客户端并选择满足我的条件的客户端来完成我想做的事情,但是我希望可以通过一个范围来实现。这是模型级别的函数:

def self.purchased_after_maintenance
    clients = Client.transitioned_to_maintenance.has_purchases
    final = []

    clients.each do |client|
        min_date = client.program_transitions.to_maintenance.first.created_at
        final << client if client.purchases.last.created_at >= ? min_date
    end
end

这是否可以与作用域一起使用,而无需遍历所有客户端?

ruby-on-rails associations
1个回答
0
投票

解决主要问题之前的几件事。并不是要烦人,不要先解决主要问题,但我认为这可能会有所帮助。

  1. 代替scope :transitioned_to_maintenance, -> { where(id: ProgramTransition.to_maintenance.pluck(:client_id)) },我会这样做(请参见下文)。 merge非常强大,最近我们在公司中使用了很多东西。
scope :transitioned_to_maintenance, -> { joins(:program_transitions).merge(ProgramTransition.to_maintenance) }
  1. 不是执行scope :has_purchases, -> { where(id: Purchase.pluck(:client_id)) },而是执行scope :has_purchases, -> { joins(:purchases) },因为购买的联接只会返回具有关联购买的客户。

关于主要问题,我想不出使用范围来实现此目标的好方法,但可能是可行的。如果您是我,我会问您的数据团队该查询的SQL外观如何,然后尝试找出创建该查询所需的活动记录。但是,如果您坚持使用自己的方法,我建议您稍作调整。使用select而不是each并渴望加载以避免n + 1个查询。

def self.purchased_after_maintenance
  Client.joins(:purchases, :program_transitions).includes(:purchases, :program_transitions).transitioned_to_maintenance.select do |client|
    transition_date = client.program_transitions.to_maintenance.first.created_at
    client.purchases.last.created_at >= transition_date 
  end
end

希望这会有所帮助,很抱歉,我对范围没有答案。

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