【问题标题】:ROR - How to use child Model's scope in Parent model's scopeROR - 如何在父模型的范围内使用子模型的范围
【发布时间】:2020-07-20 15:04:50
【问题描述】:
class Application < ActiveRecord::Base
  has_many :documents, class_name: DocumentTracker.name
  scope :with_pending_docs, -> {
    includes(:documents) {
      # not able to use DocumentTracker's scope here
      DocumentTracker.status_pending
    }
  }
  #...
  end

class DocumentTracker < ActiveRecord::Base
  enum: status[:pending, :rejected, :accepted]
  scope :status_pending, -> {
      where(status: statuses[:pending])
    }
  }
  #...
end

我想执行类似的操作:

application = Application.with_pending_docs.find(100)
application.documents.each{ |document|  
  # do something with pending docs
}

我可以单独执行此操作,但这会触发其他查询,例如:

application = Application.includes(:documents).find(100) #(2 queries)
docs = application.documents.status_pending #(+1 query)

我可以对单个记录执行此操作,但它不会达到目的(单个+多个记录查询):

application = Application.find(100).documents.status_pending

在处理多个应用程序时可能会遇到 N+1 查询问题,因此希望一次性完成

也许,我缺少次要语法或格式,但无法使用谷歌搜索找到任何相关内容

红宝石:2.4.1 导轨:5.1.0

【问题讨论】:

    标签: ruby-on-rails ruby activerecord ruby-on-rails-5 rails-activerecord


    【解决方案1】:

    您需要执行类似的操作,就好像您将使用带有条件的 left outer join 在纯 SQL 中编写查询:

    class Application < ActiveRecord::Base
      has_many :documents, class_name: DocumentTracker.name
      has_many :pending_documents, -> { where({ status: 'pending' }) }, class_name: DocumentTracker.name
      scope :with_pending_docs, -> { includes(:pending_documents).where.not(document_trakers: { id: nil }) }
    end
    

    然后您可以在Application 的每个实例上调用pending_documents 而不会收到N+1 个查询:

    Application.with_pending_docs.each { |app| p app.pending_documents }
    

    【讨论】:

    • 虽然我可以做到,但我正在寻找一种方法来在父模型中重用子记录的范围,并适用于单个/多个应用程序记录)
    • 我很想知道如何直接从父模型中使用子范围。我希望有某种方法可以做到这一点,并且隐藏在某个地方。恕我直言,为同一模型创建两个 has_many 关系也可能会造成混淆
    • 不清楚你想要实现什么。有两个关系是很正常的。
    【解决方案2】:

    在尝试了很多关键字之后,最终得到了以下答案: https://stackoverflow.com/a/41383726/2902520

    我所做的总结(请注意,我已经在 Application 和 DocumentTracker 中各有一个范围)

    #Application Scope
    scope :with_pending_docs, -> {
        includes(:documents).
          merge(DocumentTracker.status_pending)
         .references(:documents)
    }
    

    查询:

    Application.with_pending_docs.find(100)
    #or
    Application.with_pending_docs.where("applications.id > ?", 100)
    

    希望这对将来的人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-09
      • 2019-05-29
      • 1970-01-01
      相关资源
      最近更新 更多