#ruby-on-rails #activerecord
#ruby-on-rails #activerecord
Вопрос:
У меня есть модель, для которой я хочу получить следующую (ые) и предыдущую (ые) записи. Я хочу сделать это через named_scope в модели, а также передать в качестве аргумента X количество следующих / предыдущих записей для возврата.
Например, допустим, у меня есть 5 записей:
- Запись 1
- Record2
- Запись 3
- Record4
- Запись 5
Я хочу иметь возможность вызывать Model.previous или Model.previous(1), чтобы вернуть Record2. Аналогично, я хочу иметь возможность вызывать Model.next или Model.next(1), чтобы возвращать Record4. В качестве другого примера я хочу иметь возможность вызывать Model.previous(2) для возврата Record3. Я думаю, вы поняли идею.
Как я могу этого добиться?
Ответ №1:
Чтобы реализовать что-то вроде ‘Model.previous’, сам класс должен был бы иметь ‘текущее’ состояние. Это имело бы смысл, если бы «текущая» запись (возможно, в системе планирования публикации?) в вашем примере была Record3, но ваш пример этого не предполагает.
Если вы хотите взять экземпляр модели и получить следующую или предыдущую запись, ниже приведен простой пример:
class Page < ActiveRecord::Base
def previous(offset = 0)
self.class.first(:conditions => ['id < ?', self.id], :limit => 1, :offset => offset, :order => "id DESC")
end
def next(offset = 0)
self.class.first(:conditions => ['id > ?', self.id], :limit => 1, :offset => offset, :order => "id ASC")
end
end
Если это так, вы могли бы сделать что-то вроде:
@page = Page.find(4)
@page.previous
Также работало бы:
@page.previous(1)
@page.next
@page.next(1)
Очевидно, это предполагает, что идея ‘next’ и ‘previous’ заключается в поле ‘id’, которое, вероятно, не очень распространялось бы на срок службы приложения.
Если вы действительно хотели использовать это в классе, возможно, вы могли бы расширить это до именованной области видимости, которая принимает «текущую» запись в качестве аргумента. Что-то вроде этого:
named_scope :previous, lambda { |current, offset| { :conditions => ['id < ?', current], :limit => 1, :offset => offset, :order => "id DESC" }}
Что означает, что вы могли бы вызвать:
Page.previous(4,1)
Где ‘4’ — это идентификатор записи, с которой вы хотите начать, а 1 — это номер, с которого вы хотите вернуться назад.
Комментарии:
1. Как насчет случая, когда ‘next’ и ‘previous’ находятся в поле ‘created_at’?
2. Просто измените запросы так, чтобы все ссылки на ‘id’ (включая предложение :order) были изменены на ‘created_at’, и все должно работать нормально.
Ответ №2:
Я добавил эту функцию в свой gem by_star в прошлом месяце. В нем могут быть другие функции, которые вас интересуют, так что проверьте это.
Это работает, когда у вас есть существующая запись, а затем вы вызываете для нее previous
или next
:
Page.find(:last).previous
Комментарии:
1. Этот драгоценный камень выглядит гладко. Попытался установить ее, но возникли некоторые проблемы. Есть какая-нибудь документация, в которой говорится, как ее запустить?
2. Нужно ли вам использовать инструкцию include или require?
3. @keruilin: С какой именно проблемой вы столкнулись?
Ответ №3:
Я написал gem для выполнения этого для произвольных условий заказа, order_query:
class Issue < ActiveRecord::Base
include OrderQuery
order_query :order_display, [
[:priority, %w(high medium low)],
[:valid_votes_count, :desc, sql: '(votes - suspicious_votes)'],
[:updated_at, :desc],
[:id, :desc]
]
def valid_votes_count
votes - suspicious_votes
end
end
Issue.order_display #=> ActiveRecord::Relation<...>
Issue.reverse_order_display #=> ActiveRecord::Relation<...>
# get the order object, scope default: Post.all
p = Issue.find(31).order_list(scope) #=> OrderQuery::RelativeOrder<...>
p.before #=> ActiveRecord::Relation<...>
p.previous #=> Issue<...>
p.position #=> 5
p.next #=> Issue<...>
p.after #=> ActiveRecord::Relation<...>