Rails: Пользовательский порядок записей - PullRequest
21 голосов
/ 30 августа 2009

Предоставляет ли ruby ​​или rails метод для заказа строк в указанном порядке? Скажите, что у меня есть следующие приоритеты: «Сильный, Высокий, Средний, Низкий».

Эти приоритеты не будут часто меняться (если вообще будут). У меня есть модель задач с приоритетным столбцом:

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

Я бы хотел получить массив всех задач, упорядоченных по приоритету. Поскольку логический порядок не соответствует алфавитному порядку, невозможно просто упорядочить по приоритетному столбцу:

Task.all(:order => :priority)

Что я сделал, так это создал модель Приоритета и определил ассоциации: Задача принадлежит_ Приоритету. В таблице приоритетов я назначил каждому названию приоритета значение и упорядочил его по этому значению. Есть лучший способ сделать это? Я бы предпочел не иметь таблицы приоритетов вообще и объявлять константу PRIORITY (как хеш), или просто указывать приоритет в виде строки в таблице задач.

Ответы [ 7 ]

26 голосов
/ 30 августа 2009

Вы можете использовать оператор case в предложении where. Это немного некрасиво, но должно работать:

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 вы можете добавить столбец позиции в виде целого числа, по которому можно отсортировать данные, и для облегчения этого существуют плагины.

13 голосов
/ 29 ноября 2014

Вот обновленная версия, которая работает для рельсов 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) }
5 голосов
/ 30 августа 2009

Я бы использовал целые числа в дБ. Проще отсортировать и проиндексировать, а затем переопределить атрибуты getter и setter для использования символов для внешнего интерфейса.

4 голосов
/ 30 августа 2009

Если изменение схемы для использования целочисленного приоритета не является вариантом, вы также можете определить собственный метод <=> в 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).

4 голосов
/ 30 августа 2009

Переключитесь на целое число, как сказал Аарон, тогда вы, вероятно, захотите использовать default_scope .

Например:

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

При использовании области по умолчанию вам не нужно указывать порядок сортировки при любом вызове метода find - задачи будут автоматически отсортированы.

2 голосов
/ 30 августа 2009

Просто чтобы решить конкретный вопрос: вы можете заказать его по последней букве.

Север (e), Hig (h), Mediu (m), Lo (w)

вы можете сделать это либо в rails, либо в sql:

order by RIGHT(priority, 1)

Я выбрал этот подход, потому что

  1. это самый простой способ.
  2. Вы упомянули, что это не изменится.

подход может быть недостаточно гибким, но я тоже стараюсь не обобщать проблему, если в этом нет необходимости

1 голос
/ 30 августа 2009

Я бы предложил использовать act_as_list (http://github.com/rails/acts_as_list/tree/master),), который добавит поле позиции к вашему объекту, но также предоставит вам все методы для перемещения объектов вверх и вниз по списку при сохранении правильного порядка.

Если приоритеты никогда не изменятся, может быть лучше сделать приоритет полем в таблице задач. Проблема в том, что вам нужно иметь возможность сортировать по приоритету. Для этого у вас есть несколько вариантов, но все, что вы выберете, должно быть сделано БД.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...