2014-09-04 7 views
11

Ho bisogno di ottenere gli oggetti record attivi precedenti e successivi con Rails. L'ho fatto, ma non so se è il modo giusto per farlo.Rails il modo migliore per ottenere l'oggetto record attivo precedente e successivo

quello che ha:

Controller:

@product = Product.friendly.find(params[:id]) 

order_list = Product.select(:id).all.map(&:id) 

current_position = order_list.index(@product.id) 

@previous_product = @collection.products.find(order_list[current_position - 1]) if order_list[current_position - 1] 
@next_product = @collection.products.find(order_list[current_position + 1]) if order_list[current_position + 1] 

@previous_product ||= Product.last 
@next_product ||= Product.first 

product_model.rb

default_scope -> {order(:product_sub_group_id => :asc, :id => :asc)} 

Quindi, il problema qui è che ho bisogno di andare al mio database e ottenere tutti questo id per sapere chi è il precedente e il prossimo.

Provato a usare la gemma order_query, ma non ha funzionato per me e ho notato che va al database e recupera tutti i record in quell'ordine, quindi, è per questo che ho fatto lo stesso ma ottenendo solo gli id.

Tutte le soluzioni che ho trovato erano semplici querys. Ordina per id o qualcosa come un campo prioritario.

+0

Quale DB stai utilizzando? PostgreSQL? –

+0

Sì. PostgreSQL –

risposta

2

Non c'è soluzione facile pronta all'uso.

Un po 'sporco, ma il modo di lavorare è attentamente la selezione di quali condizioni ci sono per la ricerca di elementi precedenti e precedenti. Con id è abbastanza facile, dal momento che tutti id s sono diversi, e Rails Guy's answer descrive proprio questo: in next per un noto id scegliere un primo ingresso con una più grande id (se i risultati sono ordinate per id, come da default). Inoltre, la sua risposta suggerisce di inserire next e previous nella classe del modello. Fare così.

Se ci sono più criteri di ordinazione, le cose ottengono complicato. Diciamo che abbiamo un set di righe ordinato per primo dal parametro group (che può avere valori uguali su righe diverse) e poi da id (che id diverso ovunque, garantito).I risultati sono ordinate per group e poi da id (sia ascendente), in modo che possiamo forse incontrare due situazioni di ottenere l'elemento successivo, è il primo della lista che ha elementi, che (così tanti che):

  • avere lo stesso group e grande id
  • avere un grande group

Lo stesso con l'elemento precedente: è necessario l'ultimo dalla lista

  • avere la stessa group e più piccola id
  • hanno un più piccolo group

Questi recuperano rispettivamente tutti e voci precedenti. Se è necessario uno solo, utilizzare Rails first e last (come suggerito da Rails Guy) o limit(1) (e diffidare dell'ordine asc/desc).

+0

https://github.com/glebm/order_query è la soluzione semplice e immediata. – glebm

+0

@glebm L'autore ha dichiarato esplicitamente che ha provato ad usarlo. Tuttavia, 'order_query' è ** non ** una soluzione pronta all'uso, è una libreria di terze parti. Capisco la tua preoccupazione, in quanto manutentore della biblioteca, sarebbe meglio chiarire con l'autore, che cosa esattamente non ha funzionato. –

4

Penso che sarebbe più veloce farlo con solo due richieste SQL, che selezionano solo due righe (e non l'intera tabella). Considerando che l'ordine di default è ordinato per id (in caso contrario, forzare l'ordinamento da id):

@previous_product = Product.where('id < ?', params[:id]).last 
@next_product = Product.where('id > ?', params[:id]).first 

Se il prodotto è l'ultimo, poi @next_product sarà pari a zero, e se è la prima, poi, @ previous_product sarà nullo.

+0

Il problema è che il mio ordine non è dall'ID e devo mantenerlo in questo modo. default_scope -> {order (: product_sub_group_id =>: asc,: id =>: asc)} –

+0

Risulta, 'first' e' last' acually do 'LIMIT 1'. Quindi ** non carica ** tonnellate di oggetti e ne prende solo uno. Recupera in realtà solo un oggetto. * Solo una nota. * –

+0

@WilliamWeckl quindi sostituisci 'id' con il campo che stai confrontando, purché non ci siano valori di ordine uguali: più criteri possono essere ottenuti tramite un costrutto come' (> first) or (= first, > secondo) '. –

24

Scrivi questi metodi nel modello Product:

class Product 

    def next 
    self.class.where("id > ?", id).first 
    end 

    def previous 
    self.class.where("id < ?", id).last 
    end 

end 

Ora si può fare nel controller:

@product = Product.friendly.find(params[:id]) 

@previous_product = @product.next 
@next_product = @product.previous 

Si prega di provare, ma la sua non testato. Grazie

+2

Il problema è che non sto ordinando per ID. –

+2

È ancora un buon esempio su dove mettere questi metodi. Può essere usato. –

+0

Sono d'accordo. Come lo chiami nella vista? – Liroy

2

Questo è ciò che fa order_query. Si prega di provare la versione più recente, mi può aiutare se non funziona per voi:

class Product < ActiveRecord::Base 
    order_query :my_order, 
    [:product_sub_group_id, :asc], 
    [:id, :asc]  
    default_scope -> { my_order } 
end 

@product.my_order(@collection.products).next 
@collection.products.my_order_at(@product).next 

Questo viene eseguito una query di carico solo il prossimo disco. Read more on Github.

Problemi correlati