2009-08-30 23 views
18

ruby ​​o rails forniscono un metodo per ordinare stringhe in un ordine specificato? Dì che ho le seguenti priorità "Severe, High, Medium, Low".Rails: ordinazione personalizzata di record

Queste priorità non cambieranno di frequente (se non del tutto). Ho un modello Task con una colonna prioritaria:

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

Mi piacerebbe avere una matrice di tutte le attività ordinate per priorità. Dal momento che l'ordine logico non segue l'ordine alfabetico, non è possibile ordinare semplicemente la colonna di priorità:

Task.all(:order => :priority) 

Quello che ho fatto è creare un modello di priorità e definito le associazioni: Task belongs_to priorità. Nella tabella delle priorità, ho quindi assegnato a ciascun nome di priorità un valore e ordinato da quel valore. C'è un modo migliore per farlo? Preferirei non avere una tabella delle priorità e dichiarare una costante PRIORITY (come un hash), o semplicemente specificare la priorità come una stringa nella tabella delle attività.

risposta

3

Passare a un numero intero come ha detto Aaron, quindi probabilmente si vorrà utilizzare un default_scope.

Ad esempio:

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

Con un ambito predefinito non sarà necessario specificare il tipo di ordinamento in una delle tue chiamate metodo Find - Le attività verranno automaticamente essere ordinati.

+0

Grande, grazie! Esattamente quello che stavo cercando. – Homar

+1

Ancora meglio sarebbe usare act_as_list con questo, che fornisce alcuni metodi di ordinamento e così via, davvero pulito. –

4

Vorrei utilizzare numeri interi in db. È più facile ordinare e indicizzare, quindi sovrascrivere l'attributo getter e setter per utilizzare i simboli per interfacciare esternamente.

1

Suggerirei di utilizzare act_as_list (http://github.com/rails/acts_as_list/tree/master), che aggiungerà un campo di posizione all'oggetto ma fornirà anche tutti i metodi per spostare le cose in alto e in basso nell'elenco pur preservando l'ordine correttamente.

Se le priorità non cambieranno mai, potrebbe essere preferibile rendere prioritario un campo nella tabella delle attività. Il problema è che devi essere in grado di ordinare per priorità. Per questo hai alcune opzioni, ma qualunque cosa tu scelga dovrebbe essere fatta dal db.

1

Solo per risolvere la tua domanda specifica: puoi ordinarla con l'ultima lettera.

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

è possibile farlo in rotaie o SQL:

order by RIGHT(priority, 1) 

scelgo questo approccio perché

  1. è il modo più semplice possibile.
  2. hai detto che non cambierà.

l'approccio potrebbe non essere abbastanza flessibile, ma anche a me, cerco di non generalizzare il problema a meno che non sia necessario

+5

Questa è la cosa meno ovvia che abbia mai visto. Vi raccomando assolutamente contro, visto che tra 6 mesi tornerete e penserete "wtf è questo?". –

+0

Non sono d'accordo. La soluzione avrà fatto e andare avanti. Rifiniscilo se sorgono ulteriori requisiti e la soluzione non è più adatta. Non mi preoccupo troppo di cosa accadrà dopo 6 mesi. questo è il problema di domani ecco un buon articolo a riguardo: http://www.purpleworkshops.com/articles/simplest-thing –

+0

Una bella soluzione semplice. Tuttavia, se finisco per cambiare le priorità, sarà necessario riscriverlo. Grazie comunque! – Homar

4

Se cambiare lo schema di utilizzare una priorità intero non è un'opzione, è possibile definire anche un metodo personalizzato < => su Attività per definire l'ordinamento.

class Task 

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

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

end 

L'aspetto negativo di farlo in questo modo è che avrete bisogno di fare la cernita sul lato Ruby, piuttosto che lasciare che il database farlo per voi, che impedisca l'uso di default_scope. Il lato positivo è che ogni volta che chiami sort su una serie di attività l'ordine verrà visualizzato correttamente (oppure, se desideri un ordine personalizzato in un determinato luogo, puoi utilizzare sort_by).

+0

Grazie John! Penso che l'opzione più semplice sarebbe usare default_scope. Tuttavia, hai risolto uno dei miei altri problemi. Grazie molto. – Homar

22

È possibile utilizzare un'istruzione case nella clausola where. E 'un po' brutto, ma dovrebbe funzionare:

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 

E poi, quando si vuole che in ordine di priorità, si può fare qualcosa di simile:

Task.all.by_priority 

Naturalmente, come altri hanno scritto, è più pulito per crea una chiave esterna in una tabella Priority anziché in una colonna di stringhe di testo. Nella tabella Priorty, puoi aggiungere una colonna di posizione come numero intero da ordinare, e i plug-in esistono per semplificare la procedura.

+2

grazie funziona perfettamente ma ho dovuto cambiare "named_scope" in "scope" –

+0

Per rails 3+ Ho dovuto usare qualcosa come 'scope: order_by_priority, -> {{order: order_by_case}}' come la nuova sintassi lambda era richiesto – Hendrik

10

Ecco up versione aggiornata che funziona per rotaie 4.1 e un po 'personalizzato:

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 un chiarimento che potrebbe aiutare (avrei fatto per me). È possibile estendere l'ordine in questo modo: scope: order_by_priority, -> {order (order_by_case, "updated_at desc")} che ci dà ordinati per priorità, e quindi per ultimo aggiornato per ciascuna priorità. – Carpela