-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a way to disable JsonApi automatic pagination #1596
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,47 +2,34 @@ | |
|
||
# How to add pagination links | ||
|
||
### JSON API adapter | ||
## JSON API adapter | ||
|
||
Pagination links will be included in your response automatically as long as | ||
the resource is paginated and if you are using the ```JsonApi``` adapter. | ||
When using the `JsonApi` adapter, pagination links will be automatically included if you use [Kaminari](https://github.com/amatsuda/kaminari) | ||
or [WillPaginate](https://github.com/mislav/will_paginate) within a Rails controller: | ||
|
||
If you want pagination links in your response, use [Kaminari](https://github.com/amatsuda/kaminari) | ||
or [WillPaginate](https://github.com/mislav/will_paginate). | ||
|
||
Although the others adapters does not have this feature, it is possible to | ||
implement pagination links to `JSON` adapter. For more information about it, | ||
please see in our docs | ||
|
||
###### Kaminari examples | ||
* Using Kaminari: | ||
|
||
```ruby | ||
#array | ||
@posts = Kaminari.paginate_array([1, 2, 3]).page(3).per(1) | ||
render json: @posts | ||
|
||
#active_record | ||
@posts = Post.page(3).per(1) | ||
render json: @posts | ||
class PostsController < ApplicationController | ||
def index | ||
posts = Post.page(params[:page]).per(params[:per_page]) | ||
render json: posts | ||
end | ||
end | ||
``` | ||
|
||
###### WillPaginate examples | ||
|
||
```ruby | ||
#array | ||
@posts = [1,2,3].paginate(page: 3, per_page: 1) | ||
render json: @posts | ||
|
||
#active_record | ||
@posts = Post.page(3).per_page(1) | ||
render json: @posts | ||
``` | ||
* Using WillPaginate: | ||
|
||
```ruby | ||
ActiveModelSerializers.config.adapter = :json_api | ||
class PostsController < ApplicationController | ||
def index | ||
posts = Post.page(params[:page]).per_page(params[:per_page]) | ||
render json: posts | ||
end | ||
end | ||
``` | ||
|
||
ex: | ||
The response might look like: | ||
```json | ||
{ | ||
"data": [ | ||
|
@@ -67,34 +54,50 @@ ex: | |
} | ||
``` | ||
|
||
ActiveModelSerializers pagination relies on a paginated collection with the methods `current_page`, `total_pages`, and `size`, such as are supported by both [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate). | ||
|
||
ActiveModelSerializers pagination relies on paginated collections which define the methods `#current_page`, `#total_pages`, and `#size`. | ||
Such methods are supported by both [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate), | ||
but you can also roll out your own paginated collection by defining these methods. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't find the above changes very helpful. What was your motivation? Sell me! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At work we use neither There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, sorry, I meant the entirely of the diff in the docs, not just the comment about rolling your own :( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, some part did not really make sense e.g.: -Although the others adapters does not have this feature, it is possible to
-implement pagination links to `JSON` adapter. For more information about it,
-please see in our docs Adding this to the JSONAPI section does not provide any value. If a user is looking for JSONAPI, he will not care about this. If he's looking for JSON, he'll probably just scroll down directly. Then I think adding an explicit example instead of a small code snippet brings more value and is easier to understand. Here we know that we should use the pagination within an Rails controller: -#array
-@posts = [1,2,3].paginate(page: 3, per_page: 1)
-render json: @posts
-
-#active_record
-@posts = Post.page(3).per_page(1)
-render json: @posts
-
+class PostsController < ApplicationController
+ def index
+ posts = Post.page(params[:page]).per_page(params[:per_page])
+ render json: posts
+ end
+end It provides an example ready to use. Then the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ok, I buy that |
||
|
||
### JSON adapter | ||
If you do not want pagination links to be automatically rendered, you may disable it by setting the `ActiveModelSerializers.config.collection_serializer` config to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changing a default is kinda risky, isn't it? today, aren't all results non-paginated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default behavior does not change. The pagination links are included by default at the moment, see #1549. |
||
`ActiveModel::Serializer::NonPaginatedCollectionSerializer`. | ||
|
||
If you are using `JSON` adapter, pagination links will not be included automatically, but it is possible to do so using `meta` key. | ||
If you want to disable pagination links for a specific controller, you may set the `serializer` option to `ActiveModel::Serializer::NonPaginatedCollectionSerializer`: | ||
|
||
Add this method to your base API controller. | ||
|
||
```ruby | ||
def pagination_dict(object) | ||
{ | ||
current_page: object.current_page, | ||
next_page: object.next_page, | ||
prev_page: object.prev_page, | ||
total_pages: object.total_pages, | ||
total_count: object.total_count | ||
} | ||
``` ruby | ||
class PostsController < ApplicationController | ||
def index | ||
posts = Post.page(params[:page]).per_page(params[:per_page]) | ||
render json: posts, serializer: ActiveModel::Serializer::NonPaginatedCollectionSerializer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this seems kinda funky -- what if I want to render posts with the MyPostSerializer? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess that would be each_serializer... idk.. this api seems funky There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. render json: posts, serializer: ActiveModel::Serializer::NonPaginatedCollectionSerializer, each_serializer: MyPostSerializer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was never fan of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, just seems super verbose, and requires knowledge of our class structure. wouldn't render json: posts, each_serializer: MyPostSerializer, paginate: false be more appropriate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That one more option to maintain 😁 And I think that this use case (using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably, yeah -- even for oneoffs, you can effectively disable pagination by setting per_page really high |
||
end | ||
end | ||
``` | ||
|
||
Then, use it on your render method. | ||
### Json adapter | ||
|
||
If you are using the `Json` adapter, pagination links will not be included automatically, but it is possible to handle pagination using the `meta` option: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, maybe I misread above -- so it is disabled by default |
||
|
||
```ruby | ||
render json: posts, meta: pagination_dict(posts) | ||
class PostsController < ApplicationController | ||
def index | ||
posts = Post.page(params[:page]).per_page(params[:per_page]) | ||
render json: posts, meta: pagination_dict(posts) | ||
end | ||
|
||
private | ||
|
||
def pagination_dict(object) | ||
{ | ||
current_page: object.current_page, | ||
next_page: object.next_page, | ||
prev_page: object.prev_page, | ||
total_pages: object.total_pages, | ||
total_count: object.total_count | ||
} | ||
end | ||
end | ||
``` | ||
|
||
ex. | ||
The response might look like: | ||
```json | ||
{ | ||
"posts": [ | ||
|
@@ -113,27 +116,3 @@ ex. | |
} | ||
} | ||
``` | ||
|
||
You can also achieve the same result if you have a helper method that adds the pagination info in the meta tag. For instance, in your action specify a custom serializer. | ||
|
||
```ruby | ||
render json: @posts, each_serializer: PostPreviewSerializer, meta: meta_attributes(@post) | ||
``` | ||
|
||
```ruby | ||
#expects pagination! | ||
def meta_attributes(resource, extra_meta = {}) | ||
{ | ||
current_page: resource.current_page, | ||
next_page: resource.next_page, | ||
prev_page: resource.prev_page, | ||
total_pages: resource.total_pages, | ||
total_count: resource.total_count | ||
}.merge(extra_meta) | ||
end | ||
``` | ||
|
||
|
||
### Attributes adapter | ||
|
||
This adapter does not allow us to use `meta` key, due to that it is not possible to add pagination links. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
require 'thread_safe' | ||
require 'active_model/serializer/collection_serializer' | ||
require 'active_model/serializer/non_paginated_collection_serializer' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this can be autoloaded |
||
require 'active_model/serializer/array_serializer' | ||
require 'active_model/serializer/error_serializer' | ||
require 'active_model/serializer/errors_serializer' | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,8 +32,7 @@ def json_key | |
|
||
def paginated? | ||
object.respond_to?(:current_page) && | ||
object.respond_to?(:total_pages) && | ||
object.respond_to?(:size) | ||
object.respond_to?(:total_pages) | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I assume that every collection should respond to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. size got lost here |
||
|
||
protected | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
require 'active_model/serializer/collection_serializer' | ||
|
||
module ActiveModel | ||
class Serializer | ||
class NonPaginatedCollectionSerializer < CollectionSerializer | ||
def paginated? | ||
false | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ def initialize(*) | |
super | ||
@_links = {} | ||
@_include_data = true | ||
@_meta = nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix one ruby warning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed in master |
||
end | ||
|
||
def link(name, value = nil, &block) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
# coding: utf-8 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need |
||
# {http://jsonapi.org/format/ JSON API specification} | ||
# rubocop:disable Style/AsciiComments | ||
# TODO: implement! | ||
|
@@ -35,6 +36,7 @@ def initialize(serializer, options = {}) | |
super | ||
@include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include]) | ||
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields)) | ||
@serialization_context = options[:serialization_context] | ||
end | ||
|
||
def default_key_transform | ||
|
@@ -126,7 +128,7 @@ def success_document(options) | |
hash[:links].update(instance_options[:links]) | ||
end | ||
|
||
if is_collection && serializer.paginated? | ||
if paginate? | ||
hash[:links] ||= {} | ||
hash[:links].update(pagination_links_for(serializer, options)) | ||
end | ||
|
@@ -185,7 +187,7 @@ def fragment_cache(cached_hash, non_cached_hash) | |
|
||
protected | ||
|
||
attr_reader :fieldset | ||
attr_reader :fieldset, :serialization_context | ||
|
||
private | ||
|
||
|
@@ -234,6 +236,12 @@ def resource_objects_for(serializers) | |
[@primary, @included] | ||
end | ||
|
||
def paginate? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually wondered if this shouldn't be the concern of pagination_links = PaginationLinks.new(serializer, serialization_context)
hash[:links] = pagination_links.add_to(hash) # check if paginated and add the links in such case the same way as the |
||
!serialization_context.nil? && | ||
serializer.respond_to?(:paginated?) && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these two lines could be replaced with a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I'd rather not use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fair enough |
||
serializer.paginated? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what was insufficient about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe the serializer sometimes doesn't respond to paginated? like.. maybe it's sometimes not an array serializer? |
||
end | ||
|
||
def process_resource(serializer, primary) | ||
resource_identifier = ResourceIdentifier.new(serializer).as_json | ||
return false unless @resource_identifiers.add?(resource_identifier) | ||
|
@@ -499,7 +507,7 @@ def links_for(serializer) | |
# prs: | ||
# https://github.com/rails-api/active_model_serializers/pull/1041 | ||
def pagination_links_for(serializer, options) | ||
PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options) | ||
PaginationLinks.new(serializer.object, serialization_context).serializable_hash(options) | ||
end | ||
|
||
# {http://jsonapi.org/format/#document-meta Docment Meta} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.