Rails, Paginator et has_many associations

Par défaut, le mélange de ces 3 ingrédients ne fonctionne pas sous Rails.

La solution est là :

I dove into RoR Pagination yesterday (ActionController::Pagination), and while it’s great for paginating all data in a model, it really sucks for paginating data from an activerecord association (and by really sucks I mean doesn’t support).

Here’s the problem. Say I have a user who has a collection of photos:

class User < ActiveRecord::Base
has_many :photos, :conditions => 'photos.is_deleted = false', :order => 'photos.created_at DESC'


I want to have the ability to feed the user’s photos to the paginator. Unfortunately, the paginate function simply takes a collection id, and will not accept a set of data (an association, in this case). So, if I want to paginate photos on a user, I have to do this:

@photo_pages, @photos = paginate :photos,
:conditions => ["photos.user_id=? AND is_deleted = false", @user.id],
:order => "photos.created_at DESC"

What a mess. Now i’ve lost the benefits of my association, since I have to define the association as part of the pagination rules. Very suprised Rails handles things this way, as it seems to violate the basic DRY principles. Anyways, I only had to write code like this a few times to realize how much of a pain in the ass it is, and I created a “paginate_association” method to help me out.

def paginate_association(object, assoc_name, find_options = {}, per_page = 10)
page = (params[:page] || 1).to_i

item_count = object.send(assoc_name.to_s + '_count')
offset = (page-1)*per_page

@items = object.send(assoc_name).find(:all, {:offset => offset, :limit => per_page}.merge(find_options))
@item_pages = Paginator.new self, item_count, per_page, page

return @item_pages, @items

I added this to my ApplicationController (application.rb), and now I can paginate assocations til the cows come home.

@photo_pages, @photos = paginate_association @user, :photos, :order => 'created_at'

This helper uses the Rails Pagination stuff, so you can easily use paginate or paginate_association with the same view. Great!

You can also pass additional “find” arguments, such as :order, :include, :join, etc…

Hopefully this is as useful for you as it’s been for me!


Leave a Reply

Your email address will not be published. Required fields are marked *