2015-04-21 3 views
4

У меня есть массив priority = ['HIGH', 'MEDIUM', 'LOW'], который используется для установки столбца базы данных «срочность». Я хотел бы получить данные, отсортированные по приоритету, хотя применение Task.order(:urgency) возвращает результаты в алфавитном порядке (то есть HIGH, LOW, MEDIUM).Rails - порядок значений столбца (столбец приоритета)

Я использую PostgreSQL для базы данных.

Я бы (очевидно) хотел бы, чтобы они возвращались с высокого на низкий приоритет. Есть ли простой способ реализовать это, возможно, используя позиции значений в массиве?

+1

, что система DB вы используете? MySQL? PostgreSQL? Что-то другое? – MrYoshiji

+0

PostgreSQL - извините, должен был упомянуть. Обновится! – SRack

+2

Это не дает прямого ответа на ваш вопрос о заказе, но для «срочности» или «статуса», если вы используете Rails 4.1+, рассмотрите возможность использования записей Active Record ([Примечания к выпуску] (http://guides.rubyonrails.org/ 4_1_release_notes.html # active-record-enums) и [Документация] (http://api.rubyonrails.org/v4.1.0/classes/ActiveRecord/Enum.html)). Плюсы в том, что они бесплатно предоставляют вам области, и вы можете поставить свою «срочность» в нужном вам порядке (фактический столбец db - целое число) - вы можете заказать на основе этого, который должен выполнять намного быстрее, чем SQL Оператор CASE. При желании я могу предоставить полный пример. – marksiemers

ответ

6

Простой CASE WHEN может сделать трюк (PostGreSQL синтаксис, используемый здесь):

scope :urgency_ordered, order("CASE tasks.urgency WHEN 'HIGH' THEN 'a' WHEN 'MEDIUM' THEN 'b' WHEN 'LOW' THEN 'c' ELSE 'z' END ASC, id ASC") 

Вызов это таким образом:

Task.urgency_ordered 

Хороший выход SQL CASE WHEN:

CASE tasks.urgency 
    WHEN 'HIGH' THEN 'a' 
    WHEN 'MEDIUM' THEN 'b' 
    WHEN 'LOW' THEN 'c' 
    ELSE 'z' 
END ASC, id ASC 
+0

Работает очарование - спасибо MrYoshiji! Я должен был обернуть свою область в лямбда, как таковой: scope: urgency_ordered, -> {order ("CASE tasks.urgency WHEN" HIGH 'THEN' a 'WHEN ' MEDIUM 'THEN' b 'WHEN' LOW ' THEN 'c' ELSE 'z' END ASC, id ASC ")}' (в случае, если кто-то еще должен использовать его таким же образом), но он отлично работает! – SRack

+1

Вам не нужно использовать лямбда: лямбда для области видимости необходима только тогда, когда вам нужны динамические значения в вашей области, такие как 'Date.today' или' user' (потому что результат области зависит от переменная). Здесь, в вашем случае, предложение order не зависит от переменной, оно всегда совпадает с тем, как Task будет упорядочен. – MrYoshiji

+0

Ах, имеет большой смысл. Причина, по которой я внес изменения, заключалась в том, что я получаю сообщение об ошибке «Объем тела должен быть вызываемым». Без него, и маленький googling сказал мне, что лямбда исправит это. Это было сделано в этом случае, хотя ваше обоснование имеет для меня большой смысл. Для моего собственного любопытства, вы знаете, почему это может быть? (Не волнуйтесь, если нет, моя проблема решена в конце концов!) – SRack

2

При использовании Rails 4.1+, рассмотрите возможность использования активных перечислений Record (Release Notes и Documentation) для агностика решения БД:

class Task < ActiveRecord::Base 
    enum priority: [ :high, :medium, :low ] 
    # Or enum priority: { high: 0, medium: 1, low: 2 } 

    scope :priority_order, ->{ order(:priority) } 
    scope :reverse_priority_order, ->{ order(priority: :desc) } 
end 

Это потребует новый целочисленный столбец для приоритета и копирования/перемещения текстовых данных, которые вам должны иметь новый целочисленный столбец, который можно было бы выполнить при миграции. то вызов Task.priority_order должен сделать трюк.

+0

Это выглядит отличным решением. Если бы я мог отметить два ответа ... Спасибо за вклад, я определенно буду помнить это в будущем! – SRack

+1

Np, ответ г-на Йошиджи - хороший, он должен работать с любой версией Rails и не требует изменения других частей вашего кода модели и/или представлений. Если вам посчастливилось работать с 4.1+, мне нравится работать с функцией enum. Если вы решите пойти по этому маршруту в будущем, я могу опубликовать миграцию - мне просто нужно будет получить более подробную информацию о вашем существующем коде (чтобы не нарушать существующие функции). – marksiemers

2

Поздно, но здесь мои 2центы. (Rails 4.2.1)

Если вы используете решение непосредственно так:

class Task < ActiveRecord::Base 
    enum priority: { high: 0, medium: 1, low: 2 } 
    scope :priority_order, order(:priority) 
end 

Вы получите эту ошибку: The scope body needs to be callable.

Вы должны назвать сферу, как это.

scope :priority_order, -> { 
    order(:priority) 
} 

определяют его как Proc или Lambda.

Почему, вы можете спросить :)

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

+0

Хорошая уловка, если я предлагаю функцию Rails 4.1+, я предполагаю, что я должен предоставить Rails 4.0+ совместимые области! – marksiemers

0

Я опаздываю на вечеринку, но я удивлен, что никто не придумал этот ответ: если вы используете MySQL (непроверенный, но должен работать и для Postgres), существует более короткий, более безопасный и более общий способ чем любой из предыдущих ответов. Он работает с любым полем и защищен от SQL-инъекции, поэтому вы можете передавать любой список значений от пользователя.

Добавьте следующую сферу вашей модели или ApplicationRecord:

class Task < ActiveRecord::Base 
    scope :order_by_field, ->(field, values) { 
    order(sanitize_sql_for_order(["field(#{field}, ?)", values])) 
    } 
end 

Теперь Вы можете позвонить в сферу прямо на связи:

tasks.ordered_by_field(:priority, ["high", "medium", "low"]) 
Смежные вопросы