Rails: Get next / previous record
Try this:
class User
has_many :photos
end
class Photo
belongs_to :user
def next
user.photos.where("id > ?", id).first
end
def prev
user.photos.where("id < ?", id).last
end
end
Now you can:
photo.next
photo.prev
Get the Next/Previous record in ActiveRecord on Ruby on Rails
I did try and error and found below is one of the solutions.
code is here.
def next
self.class.unscoped.where("updated_at <= ? AND id != ?", updated_at, id).order("updated_at DESC").first
end
def previous
self.class.unscoped.where("updated_at >= ? AND id != ?", updated_at, id).order("updated_at ASC").first
end
test is here.
[210] pry(main)> Youtube.find(100)
Youtube Load (0.8ms) SELECT "youtubes".* FROM "youtubes" WHERE "youtubes"."id" = $1 ORDER BY updated_at DESC LIMIT 1 [["id", 100]]
=> #<Youtube id: 100, author_id: 5, category_label: nil, generated_by: 1, title: "Woman's Profane Dunkin Donuts Rant Goes Viral", video_id: "-aqN7KdWgQE", created_at: "2013-09-30 18:19:42", updated_at: "2013-10-27 00:47:37", subtitles: nil>
[211] pry(main)> Youtube.find(100).next
Youtube Load (0.7ms) SELECT "youtubes".* FROM "youtubes" WHERE "youtubes"."id" = $1 ORDER BY updated_at DESC LIMIT 1 [["id", 100]]
Youtube Load (95.9ms) SELECT "youtubes".* FROM "youtubes" WHERE (updated_at <= '2013-10-27 00:47:37.241076' AND id != 100) ORDER BY updated_at DESC LIMIT 1
=> #<Youtube id: 99, author_id: 6, category_label: nil, generated_by: 1, title: "Editing physical locations in Google Maps", video_id: "-amPC4fcY0U", created_at: "2013-09-30 18:19:42", updated_at: "2013-10-27 00:47:36", subtitles: nil>
[212] pry(main)> Youtube.find(100).next.previous
Youtube Load (0.7ms) SELECT "youtubes".* FROM "youtubes" WHERE "youtubes"."id" = $1 ORDER BY updated_at DESC LIMIT 1 [["id", 100]]
Youtube Load (68.8ms) SELECT "youtubes".* FROM "youtubes" WHERE (updated_at <= '2013-10-27 00:47:37.241076' AND id != 100) ORDER BY updated_at DESC LIMIT 1
Youtube Load (79.5ms) SELECT "youtubes".* FROM "youtubes" WHERE (updated_at >= '2013-10-27 00:47:36.162671' AND id != 99) ORDER BY updated_at ASC LIMIT 1
=> #<Youtube id: 100, author_id: 5, category_label: nil, generated_by: 1, title: "Woman's Profane Dunkin Donuts Rant Goes Viral", video_id: "-aqN7KdWgQE", created_at: "2013-09-30 18:19:42", updated_at: "2013-10-27 00:47:37", subtitles: nil>
[213] pry(main)> Youtube.find(100) === Youtube.find(100).next.previous
Youtube Load (0.8ms) SELECT "youtubes".* FROM "youtubes" WHERE "youtubes"."id" = $1 ORDER BY updated_at DESC LIMIT 1 [["id", 100]]
Youtube Load (4.8ms) SELECT "youtubes".* FROM "youtubes" WHERE "youtubes"."id" = $1 ORDER BY updated_at DESC LIMIT 1 [["id", 100]]
Youtube Load (99.7ms) SELECT "youtubes".* FROM "youtubes" WHERE (updated_at <= '2013-10-27 00:47:37.241076' AND id != 100) ORDER BY updated_at DESC LIMIT 1
Youtube Load (79.6ms) SELECT "youtubes".* FROM "youtubes" WHERE (updated_at >= '2013-10-27 00:47:36.162671' AND id != 99) ORDER BY updated_at ASC LIMIT 1
=> true
Rails best way to get previous and next active record object
There's no easy out-of-the-box solution.
A little dirty, but working way is carefully sorting out what conditions are there for finding next and previous items. With id
it's quite easy, since all id
s are different, and Rails Guy's answer describes just that: in next
for a known id
pick a first entry with a larger id
(if results are ordered by id
, as per defaults). More than that - his answer hints to place next
and previous
into the model class. Do so.
If there are multiple order criteria, things get complicated. Say, we have a set of rows sorted by group
parameter first (which can possibly have equal values on different rows) and then by id
(which id different everywhere, guaranteed). Results are ordered by group
and then by id
(both ascending), so we can possibly encounter two situations of getting the next element, it's the first from the list that has elements, that (so many that):
- have the same
group
and a largerid
- have a larger
group
Same with previous element: you need the last one from the list
- have the same
group
and a smallerid
- have a smaller
group
Those fetch all next and previous entries respectively. If you need only one, use Rails' first
and last
(as suggested by Rails Guy) or limit(1)
(and be wary of the asc/desc ordering).
Rails 4: get next / previous record of an object that belongs to another object
Your solution with next
and previous
commands relative to date is good, but doesn't work completely because you need to order
the results chronologically as well. The where
will filter the ones you don't want, but you need to make sure that the rest are in the order you desire.
So you'd have something like:
def next
calendar.posts.where("time > ?", time).order(:time).first
end
def previous
calendar.posts.where("time < ?", time).order(time: :desc).first
end
Edit:
I'm assuming that time is a DateTime. If it is a Time ONLY without date information, you'll urgently want to change that to a DateTime field.
Get next/previous record in Collection by Date in Rails (and handle multiple dates on the same day)
I used the method found here: Rails 5: ActiveRecord collection index_by
Which meant adding a default scope and changing GoalTask.rb to:
default_scope { order('taskduedate ASC') }
def next_goal_task
index = goal.goal_tasks.index self
goal.goal_tasks[index + 1]
end
def last_goal_task
index = goal.goal_tasks.index self
goal.goal_tasks[index -1]
end
Rails: Previous and Next record from previous query
So, after much trial and error, here is what I came up with:
In my Photo model:
# NEXT / PREVIOUS FUNCTIONALITY
def previous(query)
unless query.nil?
index = query.find_index(self.id)
prev_id = query[index-1] unless index.zero?
self.class.find_by_id(prev_id)
end
end
def next(query)
unless query.nil?
index = query.find_index(self.id)
next_id = query[index+1] unless index == query.size
self.class.find_by_id(next_id)
end
end
This method returns the next and previous record from a search or a particular folder view by accepting an array of those records ids. I generate that ID in any controller view that creates a query view (ie the search page and the browse by folders page):
So, for instance, my search controller contains:
def search
@search = @collection.photos.search(params[:search])
@photos = @search.page(params[:page]).per(20)
session[:query] = @photos.map(&:id)
end
And then the photo#show action contains:
if session[:query]
@next_photo = @photo.next(session[:query])
@prev_photo = @photo.previous(session[:query])
end
And lastly, my view contains:
- if @prev_photo || @next_photo
#navigation
.header Related Photos
.prev
= link_to image_tag( @prev_photo.file.url :tenth ), collection_photo_path(@collection, @prev_photo) if @prev_photo
- if @prev_photo
%span Previous
.next
= link_to image_tag( @next_photo.file.url :tenth ), collection_photo_path(@collection, @next_photo) if @next_photo
- if @next_photo
%span Next
Now it turns out this works great in regular browsing situations -- but there is one gotcha that I have not yet fixed:
Theoretically, if a user searches a view, then jumps to a photo they've generated a query in session. If, for some reason, they then browse directly (via URL or bookmark) to another photo that was part of the previous query, the query will persist in session and the related photos links will still be visible on the second photo -- even though they shouldn't be on a photo someone loaded via bookmark.
However, in real life use cases this situation has actually been pretty difficult to recreate, and the code is working very well for the moment. At some point when I come up with a good fix for that one remaining gotcha I'll post it, but for now if anyone uses this idea just be aware that possibility exists.
Link to previous/next record by specific attribute type
You can do the following:
# model
def previous_animal
self.class.order('created_at desc').where('created_at < ?', self.created_at).where(animal_type: self.animal_type).first
end
# view
<% if previous_animal = @animal.previous_animal %> # local assignment in the if condition
<%= link_to(previous_animal, {class: 'prev-page'}) do %>
<span class="glyphicon glyphicon-chevron-left"></span> Meet <span class="name"><%= previous_animal.name %></span>, the <%= animal_breed(previous_animal) %>
<% end %>
<% end %>
- The
previous_animal
method is simplified, calling.first
on an ActiveRecord::Relation can't fail, but it can returnnil
. - I used the local variable assignment in the if condition because every time you call
previous_animal
on the record it trigger a SQL query. This local variable kind of act like a cache (won't trigger the SQL query multiple times).
Find Next and previous records ordered by position column rails
class CourseStep < ActiveRecord::Base
belongs_to :step
belongs_to :course
validates_uniqueness_of :step_id, :scope => :course_id
def next_step()
Course.find(self.course.id).course_steps.order(:position).where("position >= ?", self.position).limit(1).offset(1).first
end
def previous_step()
Course.find(self.course.id).course_steps.order("position DESC").where("position <= ?", self.position).limit(1).offset(1).first
end
end
get previous and next elements within and ordered collection
The @components
object is a list of Components, not Component ids, so you have to search like this:
index = @components.find_index(@present_component)
or maybe you wanted to do this:
index = @components_ids.find_index(@present_component.id)
Related Topics
Oracle: SQL Query That Returns Rows with Only Numeric Values
How to Migrate an Existing Postgres Table to Partitioned Table as Transparently as Possible
Postgresql Query to Count/Group by Day and Display Days with No Data
Is There Any General Rule on SQL Query Complexity VS Performance
Query to Find Nᵗʰ Max Value of a Column
SQL Server Equivalent of MySQL's Now()
How to Use Null or Empty String in SQL
A Strange Operation Problem in SQL Server: -100/-100*10 = 0
Add Row to Query Result Using Select
What Is the Fastest Way to Truncate Timestamps to 5 Minutes in Postgres
How to Tell What Edition of SQL Server Runs on the MAChine
Why Do Multiple-Table Joins Produce Duplicate Rows
Fastest Way to Export Blobs from Table into Individual Files
Is It Better to Create an Index Before Filling a Table with Data, or After the Data Is in Place
Creating New Database from a Backup of Another Database on the Same Server