【问题标题】:Can I use scopes on an included model?我可以在包含的模型上使用范围吗?
【发布时间】:2019-11-22 20:07:23
【问题描述】:

我在我的 Job 模型上定义了范围,我想在 Active Record 查询中包含作业时使用它们,而不是直接写出条件和排序。

这是我的代码,但非常冗长:

@employees_and_jobs = supervisor.direct_reports.alphabetical \
.includes(:jobs) \
.where('jobs.active = true') \
.order('jobs.record_number asc, jobs.effective_date asc')

这是我希望可以工作的代码:

@employees_and_jobs = supervisor.direct_reports.alphabetical.includes(:jobs).active.sorted

范围 direct_reportsalphabetical 有效,但其他范围(activesorted)被解释为属于 Employee 模型,并给我一个错误。我希望 activesorted 被解释为属于 Job 模型。如何更改查询以显示 activesortedJob 而不是 Employee 的范围?

activesorted 范围当然是在 Job 模型上定义的,并且是通过明确引用作业来完成的(但这当然还不够):

scope :sorted, -> { order('jobs.record_number asc, jobs.effective_date asc') }
scope :active, -> { where('jobs.active = true') }

(我没想到在作用域内明确引用 jobs 会使其工作,但我尝试了以防万一,并提及它以防其他人认为它可能工作。)

如何在我的查询中指定最终范围适用于所包含的工作,而不是适用于员工?

(我意识到我可以使用默认范围解决问题,但这可能会在以后产生新问题,我正在努力避免这种情况。我更喜欢上面的详细版本而不是使用默认范围。)

类似(但不同)的问题

this question 的答案没有回答我的问题,而只是提供了一种替代方法来处理这种情况。 (但我已经上面给出的另一种方法。我有工作代码,但我试图通过在主模型和包含的模型上使用范围以非常特殊的方式提高可读性模型。)

我正在寻求一种在包含的模型上使用范围的方法,但这些答案解释了如何在上使用范围 主模型 依次包含另一个模型。两个非常不同的东西。它们的相似之处在于它们都使控制器代码更简单,但另一种方法使控制器可能不太清晰。它只是将所有的复杂性转移到一个单一的范围内(在我的例子中),它位于 Employee 模型上。我的目标是拥有可以组合在一起的非常具体的范围,每个范围都有一个非常清晰和明确定义的目的。

【问题讨论】:

  • 为什么不起作用?任何日志,错误? supervisor.direct_reports.alphabetical 的结果是什么,如果不是 Job::ActiveRecord_Relation 对象会抛出 NoMethodError
  • 范围 direct_reportsalphabetical 工作,但其他(activesorted)被解释为属于 Employee 模型,并给我一个错误。我希望 activesorted 被解释为属于 Job 模型。如何更改查询以显示 activesortedJob 而不是 Employee 的范围?
  • @SebastianPalma:我更新了我的问题以包括对我上次评论的澄清......
  • 只需在末尾添加 .references(:jobs) 以便它知道您通过包含引用的表。在这里查看更多信息:apidock.com/rails/ActiveRecord/QueryMethods/references
  • 这能回答你的问题吗? Rails includes with scope

标签: ruby-on-rails ruby activerecord


【解决方案1】:

scope 实际上只是定义类方法的语法糖。因此,与任何其他类方法一样,您只需在定义它们的类上调用您的作用域:

class Job < ApplicationRecord
  belongs_to :employee
  scope :active, -> { where(active: true) }
  scope :sorted, -> { order('jobs.record_number asc, jobs.effective_date asc') }
end

class Employee < ApplicationRecord
   has_many :jobs
   scope :with_active_jobs, ->{ include(:jobs).merge(Job.active).merge(Job.sorted) }
end

ActiveRecord::SpawnMethods#merge 可能是 AR 中最未被充分利用的功能之一。它允许您以编程方式将不同的范围混合在一起。

ActiveRecord 足够聪明,可以用.where 指定表,因此在连接中使用它没有问题(.where('jobs.active = true') 也可以正常工作)。不幸的是,.order 没有那么聪明,.order(record_number: :asc, effective_date: :asc) 会生成 ORDER BY record_number ASC, effective_date ASC,这会报错。

您也没有技术原因必须在模型中执行此操作。如果您想在那里组成范围,您可以在控制器中执行Employee.include(:jobs).merge(Job.active).merge(Job.sorted) 或任何操作。但请记住,与模型相比,控制器确实很难测试。

【讨论】:

  • 看来你也可以merge(-&gt;{Job.active.sorted})
  • @engineersmnky 是的。为什么是 lambda?
  • 只因为你可以?
猜你喜欢
  • 2018-06-26
  • 2014-01-30
  • 2022-01-20
  • 1970-01-01
  • 2020-08-24
  • 1970-01-01
  • 2023-03-28
  • 2021-12-07
  • 1970-01-01
相关资源
最近更新 更多