2009-08-30 9 views
18

¿El rubí o los rieles proporcionan un método para pedir cadenas en un orden específico? Diga que tengo las siguientes prioridades "Severo, Alto, Medio, Bajo".Rieles: pedido personalizado de registros

Estas prioridades no van a cambiar con frecuencia (si es que las hay). Tengo un modelo de tareas con una columna de prioridad:

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

Me gustaría obtener una matriz de todas las tareas ordenadas por prioridad. Dado que el orden lógico no se sigue el orden alfabético, no es posible simplemente ordenar por la columna de prioridad:

Task.all(:order => :priority) 

Lo que he hecho es crear un modelo de prioridades y define las asociaciones: Tarea belongs_to prioridad. En la tabla de prioridades, luego asigné a cada nombre de prioridad un valor y lo ordeno por ese valor. ¿Hay una mejor manera de hacer esto? Prefiero no tener una tabla de prioridades en absoluto y declarar una constante PRIORITY (como un hash), o simplemente especificar la prioridad como una cadena en la tabla de tareas.

Respuesta

3

Cambie a un número entero como Aaron dijo, entonces es probable que desee utilizar un default_scope.

Por ejemplo:

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

Con un ámbito predeterminado que no tendrá que especificar el orden de clasificación en cualquiera de sus llamadas al método find - automáticamente se ordenan las tareas.

+0

Muy bien, gracias! Exactamente lo que estaba buscando. – Homar

+1

Sería aún mejor usar acts_as_list con esto, que proporciona algunos métodos de clasificación, etc., realmente prolijo. –

4

Utilizaría números enteros en db. Es más fácil ordenar e indexar, luego anular el atributo getter y setter para usar símbolos para la interfaz externa.

1

Sugeriría usar acts_as_list (http://github.com/rails/acts_as_list/tree/master), que agregará un campo de posición a su objeto pero también le dará todos los métodos para mover las cosas hacia arriba y hacia abajo en la lista mientras conserva el orden correctamente.

Si las prioridades nunca van a cambiar, podría ser mejor hacer de la prioridad un campo en la tabla de tareas. El problema es que debes poder ordenar por prioridad. Para eso tienes algunas opciones, pero lo que elijas debe hacerlo el db.

1

Solo para resolver su pregunta específica: Puede pedirla en la última letra.

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

usted puede hacerlo en los carriles o SQL:

order by RIGHT(priority, 1) 

elijo este enfoque porque

  1. es la manera más fácil posible.
  2. has mencionado que no va a cambiar.

el enfoque podría no ser lo suficientemente flexible, pero también yo, trato de no generalizar el problema a menos que sea necesario

+5

Esa es la cosa menos obvia que he visto. Recomiendo totalmente esto, ya que dentro de 6 meses volverás y pensarás "¿qué es esto?". –

+0

No estoy de acuerdo. La solución lo hará y continuará. Reutilícelo si surgen más requisitos y la solución ya no es adecuada. No me preocupo demasiado por lo que va a pasar después de 6 meses. ese es el problema de mañana. aquí hay un buen artículo al respecto: http://www.purpleworkshops.com/articles/simplest-thing –

+0

Una buena solución simple. Sin embargo, si termino cambiando las prioridades, tendrá que ser reescrito. ¡Gracias! – Homar

4

Si al cambiar el esquema que se utiliza una prioridad número entero no es una opción, se puede también define un método personalizado < => en Tarea para definir el orden de clasificación.

class Task 

    PRIORITIES = { 
    :high => 3, 
    :med => 2, 
    :low => 1, 
    } 

    def <=> (other) 
    PRIORITIES[self.priority] <=> PRIORITIES[other.priority] 
    end 

end 

La desventaja de hacerlo de esta manera es que se necesita para realizar la ordenación en el lado Ruby, en lugar de dejar que su base de datos que lo haga por usted, que impida el uso de default_scope. Lo bueno es que cada vez que llame al sort en una matriz de tareas, la orden se publicará correctamente (o, si solo desea una orden personalizada en un lugar en particular, puede usar sort_by).

+0

Gracias John! Creo que la opción más simple sería usar default_scope. Sin embargo, has resuelto uno de mis otros problemas. Muchas gracias. – Homar

22

Puede usar una declaración de caso en su cláusula where. Es un poco feo, pero debería funcionar:

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 

Y luego, cuando usted quiere que ordena por prioridad, se puede hacer algo como esto:

Task.all.by_priority 

Por supuesto, como otros han mencionado, es más limpio para hacer una clave externa a una tabla de Prioridad en lugar de una columna de cadena de texto. En la tabla de Priorty, puede agregar una columna de posición como un entero por el que podría ordenar, y existen complementos para que sea más fácil.

+2

gracias funciona perfectamente pero tuve que cambiar "named_scope" a "scope" –

+0

Para rails 3+ Tuve que usar algo como 'scope: order_by_priority, -> {{order: order_by_case}}' como la nueva sintaxis lambda fue requerido. – Hendrik

10

Aquí es cosa versión actualizada que funcione para rieles 4.1 y un poco de personalizar:

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) } 
+0

Solo una aclaración que podría ayudar (habría hecho por mí). Puede ampliar el orden de la siguiente manera: ámbito: order_by_priority, -> {order (order_by_case, "updated_at desc")} que nos da orden por prioridad, y luego por última actualización para cada prioridad. – Carpela

Cuestiones relacionadas