Using will_paginate with ActiveRecord

The primary integration for will_paginate is with ActiveRecord, the default ORM for Ruby on Rails. It extends ActiveRecord::Base and ActiveRecord::Relation to provide simple and chainable pagination methods.

The .paginate() Method

The main method is .paginate(). It's a paginating finder that works similarly to ActiveRecord's own finders. It returns a relation that is aware of its pagination state.

Parameters

  • :page (required): The current page number to retrieve. Usually params[:page].
  • :per_page: The number of records to retrieve per page. This overrides any model-level or global settings.
  • :total_entries: Manually sets the total number of records. This is useful for performance, as it skips the COUNT query that will_paginate would otherwise perform.

Example

# Get the first page with 20 posts per page
@posts = Post.paginate(page: params[:page], per_page: 20)

# Get page 3, letting the model/global setting define per_page
@users = User.paginate(page: 3)

The .page() Shorthand

For a more modern, chainable syntax that integrates cleanly with other ActiveRecord scopes, use the .page() method.

# Chainable and clean
@posts = Post.where(published: true).page(params[:page]).order(id: :desc)

You can set the number of items per page by chaining .per_page() or .limit():

@posts = Post.page(params[:page]).per_page(5)
@posts = Post.page(params[:page]).limit(5) # `limit` is an alias for per_page

Returned Collection

Both .paginate() and .page() return an ActiveRecord::Relation instance that has been extended with WillPaginate::Collection methods. This means you can iterate over it just like a regular collection of records, but you also have access to helpful pagination attributes:

  • collection.current_page
  • collection.per_page
  • collection.total_entries
  • collection.total_pages
  • collection.offset
  • collection.previous_page
  • collection.next_page

Advanced Querying

will_paginate works seamlessly with most advanced ActiveRecord queries.

Scopes and Associations

# Paginate results from a scope
@poor_developers = Developer.poor.paginate(page: 1)

# Paginate an association
@project = Project.find(1)
@developers = @project.developers.paginate(page: 1)

Joins

You can paginate queries that include joins.

@developers = Developer.joins(:projects)
                       .where('projects.id = ?', 1)
                       .paginate(page: 1)

Grouping

Pagination with grouped queries is also supported. will_paginate is smart enough to correctly calculate the total number of entries.

@salaries = Developer.select('salary').group('salary').page(1)

# This will correctly count the number of groups, not the total number of developers.
@salaries.total_entries # => 4 (for example)

Custom SQL with paginate_by_sql

For complex queries where ActiveRecord's query interface isn't sufficient, you can use paginate_by_sql. This method takes a raw SQL string and paginates the results by adding the appropriate LIMIT and OFFSET clauses.

sql = "SELECT * FROM topics WHERE content LIKE '%ruby%'"
@topics = Topic.paginate_by_sql(sql, page: params[:page], per_page: 10)

will_paginate will attempt to generate a COUNT query automatically. If this fails or you want to optimize it, you can provide a :total_entries option.