2014-09-04 7 views
11

Мне нужно получить предыдущие и следующие активные объекты записи с помощью Rails. Я сделал это, но не знаю, правильно ли это сделать.Rails лучший способ получить предыдущий и следующий активный объект записи

То, что я получил:

Контроллер:

@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)} 

Таким образом, проблема здесь в том, что я должен пойти в свою базу данных и получить все это означает, чтобы знать, кто является предыдущим и следующим.

Пробовал использовать драгоценный камень order_query, но это не сработало для меня, и я отметил, что он отправляется в базу данных и извлекает все записи в этом порядке, поэтому я сделал то же самое, но получил только идентификаторы.

Все решения, которые я нашел, были с запросами простого заказа. Закажите по id или что-то вроде поля приоритета.

+0

Какая БД вы используете? PostgreSQL? –

+0

Да. PostgreSQL –

ответ

2

Нет простого решения из коробки.

Немного грязный, но рабочий путь тщательно разбирает, какие условия существуют для поиска следующих и предыдущих предметов. С id это довольно легко, так как все id s различны, и Rails Guy's answer описывает только что: в next для известного id выбрать первую запись с большим id (если результаты отсортированы по id, согласно умолчанию). Более того - его ответ намекает разместить next и previous в классе модели. Сделай так.

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

  • имеют же group и крупнее id
  • имеют большая group

То же самое с предыдущим элементом: вам нужен последний из списка

  • имеют же group и меньше id
  • имеют меньше group

Выбрали все следующие и предыдущие записи соответственно. Если вам нужен только один, используйте Rails «first» и «last» (как предложено Rails Guy) или limit(1) (и с осторожностью относитесь к заказу по порядку/снятию).

+0

https://github.com/glebm/order_query - это простое из готового решения. – glebm

+0

@glebm Автор прямо заявил, что пытался его использовать. Тем не менее, 'order_query' ** не является ** готовым решением, это сторонняя библиотека. Я понимаю вашу озабоченность, как хранителя библиотеки, было бы лучше уточнить с автором, что именно не сработало. –

4

Я думаю, что было бы быстрее сделать это только с двумя запросами SQL, которые выбирают только две строки (а не всю таблицу). Учитывая, что ваш заказ по умолчанию отсортированы по идентификатору (в противном случае, сила сортировки по идентификатору):

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

Если продукт является последним, то @next_product будет ноль, и если это первое, то, @ Предыдущий_продукт будет равен нулю.

+0

Проблема в том, что мой заказ не по id, и мне нужно поддерживать этот путь. default_scope -> {order (: product_sub_group_id =>: asc,: id =>: asc)} –

+0

Оказывается, 'first' и' last' на самом деле делают 'LIMIT 1'. Так что ** не ** загружает тонны объектов и берет только один. Он фактически извлекает только один объект. * Просто заметьте. * –

+0

@WilliamWeckl затем замените 'id' на поле, которое вы сравниваете, до тех пор, пока нет равных значений упорядочения: множественные критерии могут быть достигнуты с помощью конструкции типа' (> first) или (= first, > второй) '. –

24

Запишите эти методы в вашей Product модели:

class Product 

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

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

end 

Теперь вы можете сделать в вашем контроллере:

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

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

Пожалуйста, попробуйте, но его не проверял. Thanks

+2

Проблема в том, что я не заказываю по ID. –

+2

Это все еще хороший пример того, где можно поставить эти методы. Можно использовать. –

+0

Согласен. Как вы называете это в представлении? – Liroy

2

Это то, что order_query. Пожалуйста, попробуйте последнюю версию, я могу помочь, если он не работает для вас:

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 

Это запускает одну загрузки запросов только на следующую запись. Read more on Github.

Смежные вопросы