2009-08-30 64 views
18

Ruby或Rails是否提供了按特定順序排序字符串的方法?假設我有以下優先級:「嚴重,高,中,低」。Rails:記錄的自定義排序

這些優先級不會經常變化(如果有的話)。我有一個優先級列任務模型:

tasks 
    - id (integer) 
    - name (string) 
    - priority (string) 

我想獲得按優先級排序的所有任務的數組。由於邏輯順序不按字母順序排列,這是不可能簡單地通過優先級列順序:

Task.all(:order => :priority) 

我所做的就是創建一個優先級模型和定義的關聯:任務belongs_to的優先級。在優先級表中,我然後爲每個優先級名稱分配一個值並按該值排序。有一個更好的方法嗎?我寧願沒有一個優先級表,並聲明一個PRIORITY常量(作爲散列),或者只是將優先級指定爲任務表中的字符串。

回答

3

切換到像Aaron這樣的整數說,那麼你可能會想要使用default_scope

例如:

class Task < ActiveRecord::Base 
    default_scope :order => 'tasks.position' # assuming the column name is position 
    ... 

隨着你不會有任何的查找方法調用指定的排序順序默認範圍 - 任務會自動進行排序。

+0

很好,謝謝!正是我在找什麼。 – Homar 2009-08-30 21:14:30

+1

更好的辦法是使用acts_as_list,這爲它提供了一些排序方法等等,真的很整潔。 – 2009-08-31 07:20:49

4

我會在數據庫中使用整數。排序和索引更容易,然後重寫屬性getter和setter以使用符號來進行外部接口。

1

我會建議使用acts_as_list(http://github.com/rails/acts_as_list/tree/master),它會爲您的對象添加一個位置字段,但也會爲您提供在列表中上下移動事物的所有方法,同時正確保存訂單。

如果優先級永遠不會改變,那麼在任務表上優先設置一個字段可能會更好。問題是你需要能夠按優先級排序。爲此,你有幾個選項,但無論你選擇什麼,它應該由數據庫完成。

1

只是爲了解決你的具體問題:你可以用最後一個字母來排序。

Sever的(E),幾點措施(H),Mediu(M),羅(W)

你可以做到這一點在軌或SQL:

order by RIGHT(priority, 1) 

我選擇這種方法因爲

  1. 這是最簡單的方法。
  2. 你提到它不會改變。

的方法可能並不足夠靈活,但過於我,我儘量不一概而論的問題,除非有必要

+5

這是我見過的最不明顯的事情。我完全不建議這樣做,因爲在6個月內你會回來並認爲「wtf是這個?」。 – 2009-08-30 17:16:11

+0

我不同意。該解決方案將完成並繼續前進。如果出現更多的要求並且解決方案不再合適,則反映出來。我不擔心6個月後會發生什麼。這是明天的問題。這裏是一個很好的文章:http://www.purpleworkshops.com/articles/simplest-thing – 2009-08-30 17:48:09

+0

一個很好的簡單的解決方案。但是,如果我最終改變優先順序,則需要重新編寫。雖然謝謝! – Homar 2009-08-30 21:07:51

4

如果更改架構以使用一個整數的優先級是不是一種選擇,你可以還可以在Task上定義一個自定義< =>方法來定義排序順序。

class Task 

    PRIORITIES = { 
    :high => 3, 
    :med => 2, 
    :low => 1, 
    } 

    def <=> (other) 
    PRIORITIES[self.priority] <=> PRIORITIES[other.priority] 
    end 

end 

做這種方式的缺點是,你需要做的Ruby端排序,而不是讓你的數據庫,爲你做它,這將排除使用default_scope。好處是,只要您在任務陣列上撥打sort,訂單就會正確顯示(或者,如果您只想在特定地點進行自定義訂購,則可以使用sort_by)。

+0

謝謝John!我認爲更簡單的選擇是使用default_scope。但是,你解決了我的其他問題之一。非常感謝。 – Homar 2009-08-30 21:13:43

22

您可以在where子句中使用case語句。這是一個有點難看,但應該工作:

class Task 
    PRIORITIES_ORDERED = ['high', 'medium', 'low'] 

    # Returns a case statement for ordering by a particular set of strings 
    # Note that the SQL is built by hand and therefore injection is possible, 
    # however since we're declaring the priorities in a constant above it's 
    # safe. 
    def self.order_by_case 
    ret = "CASE" 
    PRIORITIES_ORDERED.each_with_index do |p, i| 
     ret << " WHEN priority = '#{p}' THEN #{i}" 
    end 
    ret << " END" 
    end 

    named_scope :by_priority, :order => order_by_case 

end 

然後當你希望他們按優先級排序,你可以這樣做:

Task.all.by_priority 

當然,其他人都提到,它的清潔劑對優先表而不是文本字符串列創建外鍵。在Priorty表中,您可以添加一個位置列作爲您可以排序的整數,並且存在插件以使其更容易。

+2

謝謝你,它完美的作品 ,但我不得不將「named_scope」更改爲「範圍」 – 2013-08-16 15:56:01

+0

對於rails 3 +我不得不使用'scope:order_by_priority, - > {{order:order_by_case}}'作爲新的lambda語法是必需的。 – Hendrik 2014-05-14 09:22:57

10

這裏了更新的版本爲4.1的軌道工程和一點點定製:

PRIORITIES_ORDERED = ['Unknown', 'Critical', 'Warning', 'Ok'] 
    def self.order_by_case 
    ret = "CASE" 
    PRIORITIES_ORDERED.each_with_index do |p, i| 
     ret << " WHEN status = '#{p}' THEN #{i}" 
    end 
    ret << " END" 
    end 

    scope :order_by_priority, -> { order(order_by_case) } 
+0

只是一個澄清,可能會幫助(會爲我做)。 您可以像這樣擴展排序: 作用域:order_by_priority, - > {order(order_by_case,「updated_at desc」)} 它給了我們按優先級排序,然後按照每個優先級最後更新的順序。 – Carpela 2016-10-11 17:26:34