簡體   English   中英

Rails:記錄的自定義排序

[英]Rails: Custom ordering of records

ruby 或 rails 是否提供按指定順序對字符串進行排序的方法? 假設我有以下優先級“嚴重、高、中、低”。

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

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

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

Task.all(:order => :priority)

我所做的是創建一個優先級 model 並定義關聯:Task belongs_to Priority。 在優先級表中,我為每個優先級名稱分配了一個值並按該值排序。 有一個更好的方法嗎? 我寧願根本沒有優先級表並聲明一個優先級常量(作為哈希),或者只是將優先級指定為任務表中的字符串。

您可以在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

當然,正如其他人所提到的,為Priority表而不是文本字符串列創建外鍵更為清晰。 在Priorty表中,您可以將位置列添加為可以排序的整數,並且存在插件以使其更容易。

這是更新版本,適用於rails 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) }

我會在db中使用整數。 排序和索引更容易,然后覆蓋屬性getter和setter以使用符號進行外部接口。

切換到像Aaron這樣的整數,然后你可能想要使用default_scope

例如:

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

使用默認范圍,您不必在任何find方法調用中指定排序順序 - 任務將自動排序。

如果不能選擇更改模式以使用整數優先級,則還可以在“任務”上定義自定義<=>方法以定義排序順序。

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 )。

只是為了解決您的具體問題:您可以通過最后一封信來訂購。

Sever(e),Hig(h),Mediu(m),Lo(w)

你可以在rails或sql中做到這一點:

order by RIGHT(priority, 1)

我選擇這種方法是因為

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

這種方法可能不夠靈活,但我也是這樣,除非有必要,否則我盡量不將問題概括為一般

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

如果優先級永遠不會改變,那么在任務表上優先考慮字段可能會更好。 問題是您需要能夠按優先級排序。 為此您有幾個選項,但無論您選擇什么,都應該由db完成。

這是 Rails 7 的更新版本:

Task.in_order_of(:priority, %w[Severe High Medium Low])

介紹它的文檔PR + 討論

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM