2013-10-08 155 views
3

在我的軌道4,5的項目,我有以下表 enter image description here紅寶石複雜的查詢優雅的解決方案

在這種SO question,我搜索了一個SQL查詢與實際項目狀態ID獲取項目= XX。實際上,我的意思是最大(created_at)。

我得到了我的查詢答案是

select p.* from projects p 
    left join projects_status_projects psp on (psp.project_id = p.id) 
    where created_at = (select max(created_at) from projects_status_projects 
      where project_id = p.id)  
    and project_status_id = XX 

我的模型定義

class Project < ActiveRecord::Base 
    has_many :projects_status_projects 
    has_many :projects_statuses, :through => :projects_status_projects 
end 

class ProjectStatus < ActiveRecord::Base 
    has_many :projects_status_projects 
    has_many :projects, :through => :projects_status_projects 
end 

class ProjectsStatusType < ActiveRecord::Base 
    belongs_to :project 
    belongs_to :project_status 
end 

在我項目模型,我有以下方法

def self.with_status(status) 
    joins(:projects_status_projects) 
     .where('projects_status_projects.created_at = (select max(created_at) from 
        projects_status_projects where project_id = p.id)') 
     .where('projects_status_projects.project_status_id = ?', status) 
end 

雖然查詢是正確的,但收到的結果已被很好地過濾,I f ind這個解決方案可怕並且不夠優雅。

有什麼辦法可以得到與範圍相同的結果嗎?

感謝您的幫助。

回答

0

怎麼樣?

scope :with_status, ->(status = "default_status") { 
    joins(:projects_status_projects). 
    where('projects_status_projects.project_status_id = ?', status). 
    order("projects_status_projects.created_at DESC").first 
    } 
1

你基於評論認爲

scope :with_status, -> (status) { 
    ProjectsStatusType.where(:project_status_id, status).order(:created_at).last.project 
} 

編輯的內容:

正如sockmonk說,範圍應該是可鏈接。這是一個更乾淨的方式來做到這一點,如果沒有找到項目,這也解決了問題。

# ProjectStatusType model 
scope :with_ordered_status, -> (status) { 
    where(:project_status_id, status).order(:created_at) 
} 

# Project model 
def self.find_by_status(status) 
    project_statuses = ProjectsStatusType.with_ordered_status(status) 
    project_statuses.any? ? project_statuses.last.project : nil 
end 
+0

你的方法真的很優雅,主要問題是 'nil:NilClass'的undefined方法'項目'如果請求沒有返回結果(對於這個狀態) – bmichotte

0
scope :with_status, ->(status = "default_status") { 
    joins(:projects_status_projects) 
    .where('projects_status_projects.project_status_id = ?', status) 
    .order("projects_status_projects.created_at DESC")  
} 

你打電話時,你會想釘一個「首先」到它的結束;不能在範圍本身中包含.first,因爲這會使其無法鏈接。

+0

感謝或複製和粘貼? –

+0

注意解釋。範圍需要返回一個關係,以便它們可以鏈接,所以如果.first在範圍內,它將不起作用; .first首先返回一個activerecord對象,如果沒有發現,返回nil – sockmonk

+0

也許事實上,我是一個ruby/rails新手是這個原因。但是我不知道這個範圍是否能夠返回所有具有這種狀態的項目? 我的意思是,如果我打電話像'@projects = Project.with_status(XX).first',我只會收到狀態爲XX的第一個項目? – bmichotte