繁体   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