带有关联的Rails范围 - 可选择使用关联类的属性过滤范围

时间:2014-01-15 04:30:08

标签: sql ruby-on-rails

我有一个任务管理应用程序,用户可以在其中创建“等待”任务。如果尚未收到他们正在等待的项目,则该任务仍处于活动状态。

我正在尝试将“可操作”范围设置为包含以下任务:

  • 有一个waiting_for,其中received_date不是nil(无论他们等待的是什么) OR
  • 没有等待

    class Task < ActiveRecord::Base 
    has_many :waiting_fors
    
    def self.actionable
        where("snooze_date <= ? OR snooze_date IS ?", Time.now, nil ).where(complete: false)
        .includes(:waiting_fors).where("waiting_fors.received_date IS NOT ?", nil)
    end
    
    class WaitingFor < ActiveRecord::Base
        belongs_to :task
    end
    

然而,它只是过滤到只显示一个项目有一个waiting_for并且waiting_for有一个received_date。

对于我出错的地方有任何建议吗?

更新:

这就是我现在所拥有的。如果任务要么没有等待,要么有一个等待接收,那么它就能正常工作。

scope :not_snoozed, -> {where("snooze_date <= ? OR snooze_date IS ?", Time.now, nil)}
scope :incomplete, -> {where(complete: false)}
scope :no_waiting_fors, -> {includes(:waiting_fors).where(waiting_fors: {id: nil})}
scope :not_received, -> {includes(:waiting_fors).where.not(waiting_fors: {received_date: nil})}
scope :actionable, -> {incomplete.not_snoozed.where(id: (Task.no_waiting_fors+Task.not_received).map(&:id))}

但是,如果一个任务有两个waiting_fors并且只有一个有一个收到的日期(这意味着我们还在等待一些东西),它就会显示为活动状态。

3 个答案:

答案 0 :(得分:0)

你的意思是

where("waiting_fors.received_date = ?", nil)

而不是

where("waiting_fors.received_date IS NOT ?", nil)

答案 1 :(得分:0)

所有这些都可以通过命名范围轻松完成。您可以构建不同条件的范围,然后在要使用多个条件时组合范围。你在使用Rails 4吗?这将是语法:

class Task < ActiveRecord::Base 
has_many :waiting_fors
scope :no_waiting_fors, -> {includes(:waiting_fors).where(waiting_fors: {id: nil})}
scope :not_received, -> {includes(:waiting_fors).where.not(waiting_fors: {received_date: nil})}
scope :actionable, -> {where(id: (Task.no_waiting_fors+Task.not_received).map(&:id))}
end

像这样,您可以获得灵活且可扩展的配置。

答案 2 :(得分:0)

尝试使用SQL的NOT IN功能将范围更新为:

scope :received, -> {includes(:waiting_fors).where("tasks.id not in (select tasks.id from tasks inner join waiting_fors on waiting_fors.task_id = tasks.id where waiting_fors.received_date is null)")} 
scope :actionable, -> {incomplete.not_snoozed.where(id: (Task.no_waiting_fors+Task.received).map(&:id))}