我正在使用 active_model_serializer。现在我想序列化一个带有分页的对象,我应该在控制器中还是在序列化器中进行分页逻辑?
如果我选择在序列化器中进行分页,我需要将 page_number 和 per_page 传递给序列化器。我该如何做到这一点?我的理解是序列化器只接受模型对象。
我正在使用 active_model_serializer。现在我想序列化一个带有分页的对象,我应该在控制器中还是在序列化器中进行分页逻辑?
如果我选择在序列化器中进行分页,我需要将 page_number 和 per_page 传递给序列化器。我该如何做到这一点?我的理解是序列化器只接受模型对象。
常规序列化器只关注单个项目,而不是分页列表。最直接的添加分页的方法是在控制器中进行:
customers = Customer.page(params[:page])
respond_with customers, meta: {
current_page: customers.current_page,
next_page: customers.next_page,
prev_page: customers.prev_page,
total_pages: customers.total_pages,
total_count: customers.total_count
}
但是,如果您需要对多个对象进行分页逻辑,则这相当繁琐。查看active_model_serializers文档,您将会遇到一个ArraySerializer
,用于序列化对象数组。我所做的是创建一个pagination_serializer.rb
,使用ArraySerializer
自动添加分页数组的元标记:
# my_app/app/serializers/pagination_serializer.rb
class PaginationSerializer < ActiveModel::Serializer::ArraySerializer
def initialize(object, options={})
meta_key = options[:meta_key] || :meta
options[meta_key] ||= {}
options[meta_key][:pagination] = {
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
}
super(object, options)
end
end
一旦您将PaginationSerializer
添加到rails应用程序中,您只需要在控制器中需要分页元标记时调用它:
customers = Customer.page(params[:page])
respond_with customers, serializer: PaginationSerializer
注意:我编写了这个使用Kaminari作为分页器。不过,可以很容易地修改它以与任何分页宝石或自定义解决方案配合使用。
PagedSerializer < ActiveModel::ArraySerializer
中的#as_json。def as_json(*args)
@options[:hash] = hash = {}
@options[:unique_values] = {}
hash.merge!(@options[:pagination]) if @options.key?(:pagination)
root = @options[:root]
if root.present?
hash.merge!(root => serializable_array)
include_meta(hash)
hash
else
serializable_array
end
end
- aaronmgdractive_model_serializers
版本。0.10.0 及以上版本需要使用 ActiveModel::Serializer::ArraySerializer
,而旧版本则需要使用 ActiveModel::ArraySerializer
。 - lightswitch052020更新:如果您使用json_api
模式,则active_model_serializer现在支持此功能,但文档也会教您如何添加json
模式。
如果您正在使用json_api
或json
适配器,请按照以下方式实现所需的结果。检查您正在使用的适配器类型:ActiveModelSerializers.config.adapter
。
ActiveModelSerializers.config.adapter = :json_api
)只要资源进行了分页并且您正在使用JsonApi
适配器,则分页链接将自动包含在响应中。
如果您希望在响应中包含分页链接,请使用Kaminari或WillPaginate。
#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
#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
ActiveModelSerializers.config.adapter = :json_api
ex:
{
"data": [
{
"type": "articles",
"id": "3",
"attributes": {
"title": "JSON API paints my bikeshed!",
"body": "The shortest article. Ever.",
"created": "2015-05-22T14:56:29.000Z",
"updated": "2015-05-22T14:56:28.000Z"
}
}
],
"links": {
"self": "http://example.com/articles?page[number]=3&page[size]=1",
"first": "http://example.com/articles?page[number]=1&page[size]=1",
"prev": "http://example.com/articles?page[number]=2&page[size]=1",
"next": "http://example.com/articles?page[number]=4&page[size]=1",
"last": "http://example.com/articles?page[number]=13&page[size]=1"
}
}
ActiveModelSerializers分页依赖于带有current_page
、total_pages
和size
方法的已分页集合,这些方法被Kaminari或WillPaginate支持。
如果您没有使用JSON
适配器,则不会自动包含分页链接,但可以使用meta
键来实现。
将此方法添加到您的基本API控制器中。
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
}
end
然后,在你的渲染方法中使用它。
render json: posts, meta: pagination_dict(posts)
ex.
{
"posts": [
{
"id": 2,
"title": "JSON API paints my bikeshed!",
"body": "The shortest article. Ever."
}
],
"meta": {
"current_page": 3,
"next_page": 4,
"prev_page": 2,
"total_pages": 10,
"total_count": 10
}
}
render json: @posts, each_serializer: PostPreviewSerializer, meta: meta_attributes(@posts)
#expects pagination!
def meta_attributes(collection, extra_meta = {})
{
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
}.merge(extra_meta)
end
该适配器不允许我们使用 meta
键,这导致无法添加分页链接。
ActiveModelSerializers.config.adapter = :json_api
添加到位于 config/intializers/ams.rb 文件中的代码中。 - Gregory Rayhttps://github.com/x1wins/tutorial-rails-rest-api/blob/master/lib/pagination.rb
# /lib/pagination.rb
class Pagination
def self.build_json object, param_page = {}
ob_name = object.name.downcase.pluralize
json = Hash.new
json[ob_name] = ActiveModelSerializers::SerializableResource.new(object.to_a, param_page: param_page)
json[:pagination] = {
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
}
return json
end
end
#app/controller/posts_controller.rb
#post#index
render json: Pagination.build_json(@posts)