Rails active_model_serializer与分页

Bru*_*Lin 16 serialization pagination ruby-on-rails active-model-serializers

我正在使用active_model_serializer.现在我想用分页序列化一个对象,我应该在控制器或串行器中进行分页逻辑吗?

如果我选择在序列化程序中进行分页,我需要将page_number和per_page传递给序列化程序.我该怎么办?我的理解是序列化程序只接受模型对象.

lig*_*h05 43

一次性解决方案

常规序列化程序仅涉及单个项目 - 而不是分页列表.添加分页的最直接方法是在控制器中:

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
}
Run Code Online (Sandbox Code Playgroud)

可重复使用的解决方案

但是,如果您需要多个对象的分页逻辑,这将非常繁琐.通过查看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
Run Code Online (Sandbox Code Playgroud)

一旦你已经PaginationSerializer添加到您的Rails应用程序,你需要简单的调用它,当你需要从你的控制器分页meta标签:

customers = Customer.page(params[:page])
respond_with customers, serializer: PaginationSerializer
Run Code Online (Sandbox Code Playgroud)

注意:我写这篇文章是为了使用Kaminari作为分页符.但是,它可以很容易地修改为适用于任何分页宝石或自定义解决方案.

  • @MarkMurphy我更新了我的答案,以展示如何使用ArraySerializer来清理代码并更加干燥 (8认同)
  • 我希望分页键是顶级的所以我也在``PagedSerializer <ActiveModel :: ArraySerializer`` def覆盖#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 (2认同)
  • 很好的解决方案,但不再需要,请参阅:https://github.com/rails-api/active_model_serializers/blob/master/docs/howto/add_pagination_links.md (2认同)

san*_*e89 6

2020 更新:如果您使用架构,active_model_serializer 现在支持开箱即用json_api,但文档还教您如何在使用架构时添加它json

文档位于:https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/howto/add_pagination_links.md

下面我将解释如果您使用json_apijson适配器如何获得所需的结果。检查您在 上使用的是哪一个ActiveModelSerializers.config.adapter

如果您使用的是 JSON API 适配器(您的 ActiveModelSerializers.config.adapter = :json_api)

只要资源已分页并且您正在使用适配器,分页链接就会自动包含在您的响应中JsonApi

如果您希望在回复中包含分页链接,请使用KaminariWillPaginate

雷的例子
#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
Run Code Online (Sandbox Code Playgroud) WillPaginate 示例
#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
Run Code Online (Sandbox Code Playgroud)
ActiveModelSerializers.config.adapter = :json_api
Run Code Online (Sandbox Code Playgroud)

前任:

{
  "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"
  }
}
Run Code Online (Sandbox Code Playgroud)

current_pageActiveModelSerializers 分页依赖于带有、total_pages和方法的分页集合size,例如KaminariWillPaginate都支持的方法。

如果您使用的是 JSON 适配器(您的 ActiveModelSerializers.config.adapter = :json)

如果您不使用JSON适配器,则不会自动包含分页链接,但可以使用metakey 来执行此操作。

将此方法添加到您的基础 API 控制器中。

{
  "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"
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,在渲染方法中使用它。

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
Run Code Online (Sandbox Code Playgroud)

前任。

{
  "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
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您有一个在元标记中添加分页信息的辅助方法,您也可以获得相同的结果。例如,在您的操作中指定自定义序列化程序。

render json: posts, meta: pagination_dict(posts)
Run Code Online (Sandbox Code Playgroud)
{
  "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
  }
}
Run Code Online (Sandbox Code Playgroud)

属性适配器

该适配器不允许我们使用meta密钥,因为无法添加分页链接。