19

I wonder if anyone could please tell me if will_paginate can support JSON out of the box or if this has to be hacked?

I would like to add page data to the JSON responses while will_paginate manages pagination.

6 Answers6

47

Something in the lines of:

@posts = Post.paginate :page => params[:page]

respond_to do |format|
  format.json {
    render :json => {
      :current_page => @posts.current_page,
      :per_page => @posts.per_page,
      :total_entries => @posts.total_entries,
      :entries => @posts
    }
  }
end

Once you figure out what format you want, you can always define a to_json method on a WillPaginate::Collection.

mislav
  • 14,919
  • 8
  • 47
  • 63
  • yes that is what i am wanting to do, now i know how it works it probably returns what i need by rendering straight to JSON, however, been able to change the format like this is useful. –  Jan 16 '11 at 11:48
  • 3
    check out the GitHub API docs[1] for a much better way of encoding pagination in your response (hint: put it in the headers) [1]: http://developer.github.com/v3/#pagination – asymmetric Sep 26 '12 at 10:59
  • 2
    How did you find these methods? They're not listed in the will_paginate wiki. – Francisco Quintero Jun 13 '16 at 22:20
10

Adding Pagination to an API

I found an easy solution to API Json Response Pagination with will_pagination.

start by creating a class method in ApplicationController that will create an after_filter that will set the pagination meta-data in the response headers:

application_controller.rb

class ApplicationController < ActionController::Base

  protected
  def self.set_pagination_headers(name, options = {})
    after_filter(options) do |controller|
      results = instance_variable_get("@#{name}")
      headers["X-Pagination"] = {
        total: results.total_entries,
        total_pages: results.total_pages,
        first_page: results.current_page == 1,
        last_page: results.next_page.blank?,
        previous_page: results.previous_page,
        next_page: results.next_page,
        out_of_bounds: results.out_of_bounds?,
        offset: results.offset
      }.to_json
    end
  end

end

Then in the controller we want to add pagination headers we can call it like such:

widgets_controller.rb

class Api::V1::WidgetsController < Api::V1::BaseController
  set_pagination_headers :widgets, only: [:index]

  def index
    @widgets = Widget.all.paginate(params).order("created_at desc")
    respond_with @widgets
  end

end

response headers that look like this

> Cache-Control:max-age=0, private, must-revalidate
> Connection:keep-alive Content-Type:application/json; charset=utf-8
> Etag:"fe70f7bae4c6e5cdea7867aa7fc0c7b4"
> X-Pagination:{"total":14,"total_pages":1,"first_page":true,"last_page":true,"previous_page":null,"next_page":null,"out_of_bounds":false,"offset":0}
> Server:thin 1.3.1 codename Triple Espresso
> Set-Cookie:_widgets_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJTAzYjVlNzkwZTIyNzU4YTYwMDU0M2MwOTQ2ZWI3YWU2BjsAVEkiDWxhc3RfdXJsBjsARkkiM2h0dHA6Ly9tYWluYW5kbWUtc3RhZ2luZy5oZXJva3VhcHAuY29tL3VzZXJzLzEGOwBGSSIQX2NzcmZfdG9rZW4GOwBGSSIxdjd0SEp6cVhKamh5YTh1cnBUdmpBb0w5aVA0bS9QTEdON3g1UlFUYnBkND0GOwBG--71b3a24c216a414d8db04f312b5300c818e6bba4;
> path=/; HttpOnly Transfer-Encoding:Identity
> X-Request-Id:61b383ade49cba8b24a715a453ed6e1f X-Runtime:0.191485
> X-Ua-Compatible:IE=Edge,chrome=1

Source -Adding Pagination to an API

Kaleem Ullah
  • 6,799
  • 3
  • 42
  • 47
  • This is great pagination for an API. Don't modify the returned data, place the pagination information in the headers. Super clean solution, plays nice with others like Angular who wants an array of results back for an index/list/query. – Ryan Nov 06 '16 at 23:55
3

This one helps me:

Add this method to your base API controller.

def pagination_dict(collection)
  {
    current_page: collection.current_page,
    next_page: collection.next_page,
    prev_page: collection.prev_page, # use collection.previous_page when using will_paginate
    total_pages: collection.total_pages,
    total_count: collection.total_count # use collection.total_entries when using will_paginate
  }
end

Then, use it on your render method.

render json: posts, meta: pagination_dict(posts)
dumP
  • 771
  • 8
  • 17
2

The easiest solution

  def index
    @posts =  Post.all.paginate(:page => params[:page], :per_page => 4)
    render :json => @posts.to_json(:methods => [:image_url])
  end

You just need to add gem 'will_paginate'

It works for me.

artamonovdev
  • 2,260
  • 1
  • 29
  • 33
Kaleem Ullah
  • 6,799
  • 3
  • 42
  • 47
1

will_paginate just gets the records back in a sliced manner. So it just gets an array from your database.

When you render as json, that's rails going over the array of objects and calling as_json on them.

So yes, you can will_paginate rendering out as json.

Jesse Wolgamott
  • 40,197
  • 4
  • 83
  • 109
  • But I guess i'll have to hack it so it includes the total number of pages in the JSON response along with the paginate data . –  Jan 15 '11 at 18:17
  • 1
    that's all in the response from Model.paginate -- so you'll just need to look for it in the json response. – Jesse Wolgamott Jan 15 '11 at 23:56
  • I'll also look into that. @mislav provided and example of what i was looking for, however, I can see that just rendering the standard output as JSON should also contain the information that i need. –  Jan 16 '11 at 11:43
0

Gem: will_paginate, version 3.1.8

  def pagination_meta(collection)
    {
      current_page: collection.current_page,
      next_page: collection.next_page,
      prev_page: collection.previous_page,
      total_page: collection.total_pages,
      total_count: collection.total_entries,
    } if collection.present?
  end
vidur punj
  • 5,019
  • 4
  • 46
  • 65