ico*_*ast 3 ruby activerecord ruby-on-rails
我在作业模型上定义了范围,并且我想在 Active Record 查询中包含作业时使用它们,而不是手动写出条件和排序。
这是我的代码,可以工作,但非常冗长:
@employees_and_jobs = supervisor.direct_reports.alphabetical \
.includes(:jobs) \
.where('jobs.active = true') \
.order('jobs.record_number asc, jobs.effective_date asc')
Run Code Online (Sandbox Code Playgroud)
这是我希望能够工作的代码:
@employees_and_jobs = supervisor.direct_reports.alphabetical.includes(:jobs).active.sorted
Run Code Online (Sandbox Code Playgroud)
范围direct_reports和alphabetical工作,但其他(active和sorted)被解释为属于Employee模型,并给我一个错误。我希望active并被sorted解释为属于模特Job。如何更改查询以显示 和active是和 notsorted的范围?JobEmployee
和范围当然active是sorted在 Job 模型上定义的,并且是通过对 jobs 的显式引用来完成的(但这当然还不够):
scope :sorted, -> { order('jobs.record_number asc, jobs.effective_date asc') }
scope :active, -> { where('jobs.active = true') }
Run Code Online (Sandbox Code Playgroud)
(我没想到对jobs范围内的明确引用可以使其工作,但我尝试了一下以防万一,并提及它以防其他人认为它可能工作。)
如何在查询中指定最终范围适用于所包含的职位,而不是员工?
(我意识到我可以使用默认范围来解决问题,但这可能会在以后产生新的问题,我正在努力避免这种情况。我更喜欢上面的详细版本而不是使用默认范围。)
这个问题的答案并没有回答我的问题,而只是提供了一种处理这种情况的替代方法。(但我已经有了上面给出的替代方法。我有工作代码,但我试图通过在主模型和包含的模型上使用范围来以一种非常特殊的方式提高可读性。)
我要求一种在包含的模型上使用范围的方法,但这些答案解释了如何在主模型上使用范围,而主模型又包含其他模型。两个截然不同的事情。它们的相似之处在于,它们都使控制器代码更简单,但另一种方法使控制器可能不太清晰。它只是将所有复杂性转移到一个单一的范围内,该范围(在我的例子中)位于 Employee 模型上。我的目标是拥有可以组合在一起的非常具体的范围,每个范围都有非常明确且明确定义的目的。
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
Run Code Online (Sandbox Code Playgroud)
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)如果您想在控制器中组成范围,您可以在控制器中执行任何操作。但请记住,与模型相比,控制器确实很难测试。