2015-04-21 79 views
4

我有一個數組priority = ['HIGH', 'MEDIUM', 'LOW'],它用於設置'緊急'數據庫列。我想檢索按優先級排序的數據,但應用Task.order(:urgency)可以按字母順序返回結果(即高,低,中等)。Rails - 按列的值排序(優先列)

我正在使用PostgreSQL作爲數據庫。

我會(顯然)像這些從高優先級返回到低優先級。有沒有簡單的方法來實現這個,也許使用數組中的值的位置?

+1

什麼DB系統是您使用? MySQL? PostgreSQL?還有別的嗎? – MrYoshiji

+0

PostgreSQL - 對不起,應該提到。現在更新! – SRack

+2

這並不直接回答您的訂購問題,但對於「緊急」或「狀態」,如果您使用的是Rails 4.1+,請考慮使用Active Record枚舉([發行說明](http://guides.rubyonrails.org/) 4_1_release_notes.html#active-record-enums)和[Documentation](http://api.rubyonrails.org/v4.1.0/classes/ActiveRecord/Enum.html))。優點是它爲您提供了免費的範圍,並且可以按照您需要的順序(實際的數據庫列是一個整數)放置您的「緊迫性」 - 您可以基於該數據進行排序,這應該比SQL執行速度快很多CASE聲明。如果需要,我可以提供一個完整的示例。 – marksiemers

回答

6

簡單CASE WHEN可以做的伎倆(這裏使用PostgreSQL的語法):

scope :urgency_ordered, order("CASE tasks.urgency WHEN 'HIGH' THEN 'a' WHEN 'MEDIUM' THEN 'b' WHEN 'LOW' THEN 'c' ELSE 'z' END ASC, id ASC") 

這樣調用它:

Task.urgency_ordered 

的SQL CASE WHEN的尼斯輸出

CASE tasks.urgency 
    WHEN 'HIGH' THEN 'a' 
    WHEN 'MEDIUM' THEN 'b' 
    WHEN 'LOW' THEN 'c' 
    ELSE 'z' 
END ASC, id ASC 
+0

發揮魅力 - 感謝MrYoshiji先生!我必須把我的範圍包裝在一個lambda中,例如'scope:urgency_ordered, - > {order(「CASE tasks.urgency'HIGH','''' 'MEDIUM'then'b''LOW'那麼'c'ELSE'z' END ASC,id ASC「)}'(以防其他人需要以相同的方式使用它),但它完美地完成了這項工作! – SRack

+1

您不需要使用lambda表達式:只有當您需要動態值時才需要使用lambda表達式,例如'Date.today'或'user'參數(因爲範圍的結果取決於變量)。在這裏,就你的情況而言,order子句不依賴於一個變量,它總是與任務被排序的方式相同。 – MrYoshiji

+0

啊,很有道理。我做這個改變的原因是我得到了一個'範圍體需要被調用。'沒有它的錯誤,並且有一點Google會告訴我lambda會修復它。它在這種情況下做到了 - 雖然你的理由對我很有意義。爲了我自己的好奇,你知道這可能是爲什麼嗎? (不要擔心,如果不是,我的問題畢竟解決了!) – SRack

2

如果使用Rails 4.1或更高版本,可以考慮使用的Active Record枚舉(Release NotesDocumentation)爲DB無關的解決方案:

class Task < ActiveRecord::Base 
    enum priority: [ :high, :medium, :low ] 
    # Or enum priority: { high: 0, medium: 1, low: 2 } 

    scope :priority_order, ->{ order(:priority) } 
    scope :reverse_priority_order, ->{ order(priority: :desc) } 
end 

這將需要優先一個新的整數列,以及複製/移動文本數據,你必須到新的整數列,這可以在遷移中完成。然後調用Task.priority_order應該做的伎倆。

+0

這看起來是一個很好的解決方案。如果我可以標出兩個答案......感謝您的意見,我一定會在將來記住這一點! – SRack

+1

Np,MrYoshiji的回答是一個很好的答案 - 它應該適用於任何版本的Rails,並且不需要更改模型代碼和/或視圖的其他部分。如果你足夠幸運能夠使用4.1+,我喜歡使用枚舉特性。如果您決定在未來走這條路線,我可以發佈遷移 - 我只需要一些關於您現有代碼的更多細節(不要破壞現有功能)。 – marksiemers

2

這是遲到了,但在這裏我的2cents。

class Task < ActiveRecord::Base 
    enum priority: { high: 0, medium: 1, low: 2 } 
    scope :priority_order, order(:priority) 
end 

您將收到此錯誤:The scope body needs to be callable.

你需要調用這樣的範圍如果直接使用該解決方案是這樣的(Rails的4.2.1)

scope :priority_order, -> { 
    order(:priority) 
} 

定義它像ProcLambda

爲什麼,你可能會問:)

它可以確保你有什麼塊中的每個範圍被觸發時使用。

+0

好,如果我建議使用Rails 4.1+功能,我想我應該提供Rails 4.0+兼容範圍! – marksiemers

0

我遲到了,但我很驚訝沒有人想出這個答案呢:如果你正在使用MySQL(未經測試,但也應該適用於Postgres),則有一個更短,更安全和更通用的方法比任何以前的答案。它適用於任何領域,並防止SQL注入,所以你可以傳遞來自用戶的任何值列表。

添加以下範圍模型或ApplicationRecord:

class Task < ActiveRecord::Base 
    scope :order_by_field, ->(field, values) { 
    order(sanitize_sql_for_order(["field(#{field}, ?)", values])) 
    } 
end 

現在,您可以直接調用範圍上的關係:

tasks.ordered_by_field(:priority, ["high", "medium", "low"])