Pagination - why is it this hard?

Hi everyone,

I’m at the end of my rope on this. I can’t get pagination to work
with anything but a standard find on a model. If I try to do a search
and customize the pagination, I get lots of different variations.

My thought was to have the list action do what it does, but to pass it
a list of search conditions from the search action. So, if search
determines that we need to get a list of all people in X department,
it sets up searchconditions and tries to pass it to list. No luck
there.

My basic question is, what’s the best practice way to paginate over
custom finds? The search results usually come up ok, it’s just that
the pagination links don’t pass on the right parameters, so they
either don’t work, or they just go back to the regular list, even
though the action is search (in the URL).

The most recent attempt has normal listings working correctly, but
when I perform a search (which displays correctly) and click on any of
the pagination links, since there’s no passing of the search
variables.

I know how to do this, as I’ve done it a million times in other
projects, but I can’t find the Rails way to do this that makes me say,
“Oh, magic!” And, since I’m using rails, I’d rather not go and build
this the way I would have in Perl.

I’m sorry if I come off as harsh, but I’m incredibly frustrated by
this. I’ve been hacking on it for hours now, and I can’t launch this
version of my app without proper pagination. The deadline is today,
and I figured this would be a lot easier than it seems to be.

Code:
Controller:
def list
@person_pages, @people = paginate :person, :order => ‘last_name
ASC’, :per_page => 20
end

def search
searchconditions = []
if params.include?(:letter)
searchconditions = [“LOWER(last_name) LIKE ?”,
params[:letter].to_str + “%”]
elsif params.include?(:department)
searchconditions = [“department_id = ?”, params[:department]]
elsif session[:q]
if params[:q]
session[:q] = params[:q].downcase
searchconditions = [ “LOWER(username) LIKE ? OR
LOWER(first_name) LIKE ? OR LOWER(last_name) LIKE ? OR
LOWER(preferred_name) LIKE ?”,
“%” + session[:q] + “%”,
“%” + session[:q] + “%”,
“%” + session[:q] + “%”,
“%” + session[:q] + “%” ]
end
else
session[:q] = ‘’
end
if searchconditions.empty?
flash[:note] = “You must enter a search phrase to search.”
redirect_to :action => “list”
else
@person_pages, @people = paginate :person, :per_page => 20,
:conditions => searchconditions, :order => ‘last_name ASC’
render :action => “list”
end
end

View:
<%= link_to “Previous”, { :page => @person_pages.current.previous } if
@person_pages.current.previous -%>
<% pagination_links_each(@person_pages, :window_size => 4) do |n| %>
<%= n %>
<% end %>
<%= link_to “Next”, { :page => @person_pages.current.next } if
@person_pages.current.next -%>

Resources I’ve used to (it seems) no avail:

http://api.rubyonrails.com/classes/ActionController/Pagination.html
http://wiki.rubyonrails.com/rails/pages/HowtoPagination

http://railsexpress.de/blog/articles/2005/11/04/faster-pagination

Pagination can be a little frustrating, but your example doesn’t look
too bad.

If I understand your question, you’re just having problems w/ the links
passing the correct parameters?

Just use this:
<%= link_to “Previous”, { :page => @person_pages.current.previous,
:letter => params[:letter], :department => params[:department], :q =>
params[:q] } if
@person_pages.current.previous -%>
<% pagination_links_each(@person_pages, :window_size => 4) do |n| - %>
<%= link_to n,{:page => n, :letter => params[:letter], :department
=> params[:department], :q => params[:q]} %>
<% end %>
<%= link_to “Next”, { :page => @person_pages.current.next , :letter =>
params[:letter], :department => params[:department], :q => params[:q] }
if
@person_pages.current.next -%>

The “magick” is rails will only supply parameters on the query string if
their params has a value (ie, each link won’t have
&letter=&department=&q=)

Also, your searchcondition can be more easily written as:
searchconditions = [ “LOWER(username) LIKE :q OR
LOWER(first_name) LIKE :q OR LOWER(last_name) LIKE :q OR
LOWER(preferred_name) LIKE :q”, { :q => “%#{session[:q]}%” }]

Wow, what an outpouring! Big Thank Yous to Jon, Kevin, Mark, and “ym”
on #rubyonrails. I think I’ve got this going now.

The approach I went with was to create a conditions array in my search
action, save it into a session variable, and then redirect_to the list
action. The list action checks for the variable (if nothing, it uses
“1 = 1”) and runs the find. So the conditions stay current in the
session until another search is run.

Your emails have shown me a few more approaches I’m going to need to
look at as my searches get more complex.

The bit from Kevin about making my own paginator…y’know, it was just
that phrase that changed my thinking of “hack this piece of code to
work for me” to, well, “make my own paginator.”

Again, thank you all so much. It seems like a silly little thing to
get stuck on. I really appreciate the help.

Sean

I just wanted to thank everyone again for helping me out yesterday. I
pushed the deadline by a couple of hours, but the app is launched and
working perfectly. Once again, the Rails community saves someone’s
butt. :slight_smile:

Sean