Garbage Burrito!

An internet web blog by Ben Kittrell

Rails: Paginating Collections

Rails: Paginating Collections
Ben Kittrell - 08/02/2006 12:02:00
Comments: 2

I hate pagination, it's always a big pain in the rump. Luckily Rails has helpers that make it almost effortless, for most situations. The paginate helper method will write SQL queries that offset and limit the result set. There's also the Paginator class that helps with the previous and next stuff.

The problem for me though is the extremely close ties to SQL that it imposes. I'm not complaining, it's a difficult problem to solve elegantly. But in order to filter the result set, you must pass in SQL through the :conditions parameter. I was fine with this until I had a query that joined four tables that needed pagination. I've gotten in the habit of encapsulating these types of queries in the Model, so I can re-use them.

Even if I wanted to repeat myself, I don't anticipate that this query will be very quick. The paginate helper does two queries, one to retrieve the result, and one to count the total number of records. The total is used to calculate the total number of pages for this result.

The alternative is to bring back the entire result set every time and chop it up in Ruby. Now I'm sure this makes some people cringe, but it really depends on the case. I'm searching through a maximum of 500 records. So the typical result set shouldn't be more that 100 records. The downside is obvious, but the upside is that I only have to do the query once, and now I can use any Collection as the result set.

So whether you like it or not I created a simple helper method that works similarly to paginate . In fact I was able to still use the Paginator class.

Here's the helper method. It can go in your application controller.


def paginate_collection(coll, options = {})
  # Grab params and fix em up
  per = options[:per_page].to_i
  page = (params[:page] || 1).to_i
  offset = (page - 1) * per
 
  # Create a Paginator based on the collection data
  pages = Paginator.new self, coll.size, per, page
 
  # Grab the sub-set of the collection
  paged = coll[offset..(offset + per - 1)]
 
  return [pages, paged]
end

As you can see the method returns the Paginator and the collection just like the paginate helper method.


@per_page = 20
@images = Image.search(q)
@image_pages, @images = paginate_collection @images, :per_page => @per_page

So the rest is just like before.


<%= link_to "Previous page", { :page => @image_pages.current.previous } if @image_pages.current.previous %>
 
<%= link_to "Next page", { :page => @image_pages.current.next } if @image_pages.current.next %>

I'm not super satisfied with this solution, but in my case I prefer it to the alternative. There are also cases where you may want to paginate through a collection that didn't even come from a database.

Remember on "You can't do that on television" when they'd have the kid in front of the firing squad, and they'd find a way to make that one guy say "fire"? Gotta love those Canadians.

Comments: 2

Comments

1. Danger   |   08/02/2006 14:33:58

That's some good stuff. the simple error displays and the simple pagination are both things that every developer outgrows quickly. I'm glad to see you've given us a fighting chance at integrating our traditional pagination with more complicated queries.

2. LawsonAmparo34  |  my website   |   10/07/2010 23:52:28

If you're in not good state and have got no cash to move out from that, you would have to receive the <a href="http://bestfinance-blog.com/topics/mortgage-loans">mortgage loans</a>. Because it would aid you definitely. I get student loan every year and feel myself good because of it.

Post a Comment


Please enter the word below.


powered by Doodlekit™ Free Website Builder by Doodlebit™ Website Company