Rails: Пользовательский порядок записей
Рубины или рельсы предоставляют метод упорядочения строк в указанном порядке? Скажем, что у меня есть следующие приоритеты: "Тяжелый, Высокий, Средний, Низкий".
Эти приоритеты не будут часто меняться (если вообще). У меня есть модель задачи с приоритетным столбцом:
tasks
- id (integer)
- name (string)
- priority (string)
Я хотел бы получить массив всех заданий, упорядоченных по приоритету. Поскольку логический порядок не соответствует алфавитному порядку, его невозможно просто упорядочить по столбцу приоритета:
Task.all(:order => :priority)
То, что я сделал, это создать модель Priority и определить ассоциации: Task принадлежит_приоритету. В таблице приоритетов я затем присваивал каждому имени приоритета значение и упорядочивался по этому значению. Есть лучший способ сделать это? Я бы предпочел не иметь таблицу приоритетов вообще и объявить константу PRIORITY (как хэш) или просто указать приоритет как строку в таблице задач.
Ответы
Ответ 1
Переключитесь на целое число, как сказал Аарон, тогда вы, вероятно, захотите использовать default_scope.
Например:
class Task < ActiveRecord::Base
default_scope :order => 'tasks.position' # assuming the column name is position
...
При использовании области по умолчанию вам не нужно указывать порядок сортировки в любом вызове метода поиска - задачи будут автоматически отсортированы.
Ответ 2
Вы можете использовать оператор 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
Конечно, как говорили другие люди, очистить внешний ключ от таблицы Priority вместо столбца текстовой строки. В таблице Priorty вы можете добавить столбец позиции как целое число, которое вы можете отсортировать, и существуют плагины, чтобы сделать это проще.
Ответ 3
Здесь приведена обновленная версия, которая работает для рельсов 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) }
Ответ 4
Я бы использовал целые числа в db. Легче сортировать и индексировать, а затем переопределять атрибут getter и setter для использования символов для внешнего интерфейса.
Ответ 5
Если изменение схемы для использования целочисленного приоритета не является опцией, вы также можете определить собственный метод <= > в задаче для определения порядка сортировки.
class Task
PRIORITIES = {
:high => 3,
:med => 2,
:low => 1,
}
def <=> (other)
PRIORITIES[self.priority] <=> PRIORITIES[other.priority]
end
end
Недостатком этого является то, что вам нужно выполнить сортировку на стороне Ruby, а не позволить вашей базе данных сделать это для вас, что исключает использование default_scope
. Поверхность заключается в том, что в любой момент, когда вы вызываете sort
в Array of Tasks, порядок будет выдаваться правильно (или, если вы хотите только индивидуальный заказ в одном конкретном месте, вы можете использовать sort_by
).
Ответ 6
Я бы предложил использовать act_as_list (http://github.com/rails/acts_as_list/tree/master), который добавит поле позиции к вашему объекту, но также даст вам все методы перемещения вещей вверх и вниз по списку, сохраняя порядок правильно.
Если приоритеты никогда не будут изменяться, лучше сделать приоритет поле в таблице задач. Проблема в том, что вам тогда нужно иметь возможность сортировать по приоритету. Для этого у вас есть несколько вариантов, но все, что вы выберете, должно быть сделано с помощью db.
Ответ 7
Просто чтобы решить ваш конкретный вопрос: вы можете заказать его по последней букве.
Север (e), Hig (h), Mediu (m), Lo (w)
вы можете сделать это в rails или sql:
order by RIGHT(priority, 1)
Я выбираю этот подход, потому что
- это самый простой способ.
- Вы упомянули, что он не изменится.
подход может быть недостаточно гибким, но тоже я, я стараюсь не обобщать проблему, если это необходимо