【问题标题】:Complicated scope involving multiple models涉及多个模型的复杂范围
【发布时间】:2019-12-10 13:22:47
【问题描述】:

我正在尝试创建一个范围,该范围将为我提供所有 clients 在他们拥有 :transitioned_to_maintenance(我们内部使用的术语)之后创建 purchase

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

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】:

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

    1. 而不是scope :transitioned_to_maintenance, -&gt; { where(id: ProgramTransition.to_maintenance.pluck(:client_id)) },我会这样做(见下文)。 merge 真的很强大,而且我们最近在我们公司经常使用它。
    scope :transitioned_to_maintenance, -> { joins(:program_transitions).merge(ProgramTransition.to_maintenance) }
    
    1. 我不会做scope :has_purchases, -&gt; { where(id: Purchase.pluck(:client_id)) },而是做scope :has_purchases, -&gt; { 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
    

    希望这会有所帮助,抱歉,我没有关于示波器的答案。

    【讨论】:

      猜你喜欢
      • 2011-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多