Rails active_model_serializer с нумерацией страниц

Я использую active_model_serializer. Теперь я хочу сериализовать объект с разбиением на страницы, должен ли я выполнять логику разбивки на страницы в контроллере или в сериализаторе?

Если я выбираю разбиение на страницы в сериализаторе, мне нужно передать номер страницы и per_page в сериализатор. Как мне это сделать? Насколько я понимаю, сериализатор принимает только объект модели.


person Bruce Xinda Lin    schedule 08.04.2014    source источник
comment
Действительно непонятно, при чем здесь сериализация. Что вы сериализуете? Как это связано с пагинацией? Эти два полностью разделены, я не могу представить, какое отношение одно имеет к другому.   -  person meagar    schedule 09.04.2014
comment
@meagar Я пытаюсь сериализовать альбом, в котором хочу разбить фотографии на страницы.   -  person Bruce Xinda Lin    schedule 09.04.2014
comment
Вы пытаетесь сказать, что ваш результат представляет собой массив, а will_paginate не работает?   -  person Ruby Racer    schedule 09.04.2014
comment
@StavrosSouvatzis Извините за путаницу. На самом деле я использую Kaminari, и нумерация страниц работает хорошо. Мой вопрос заключается в том, куда поместить логику разбивки на страницы - я должен поместить ее в контроллер или я должен поместить ее в сериализатор.   -  person Bruce Xinda Lin    schedule 09.04.2014
comment
Просто чтобы прояснить для будущих читателей, AMS теперь автоматически включает нумерацию страниц при использовании гемов Kaminari или WP: github.com/rails-api/active_model_serializers/blob/master/docs/   -  person rmcsharry    schedule 08.09.2016
comment
@rmcsharry К вашему сведению, но Link Rot начал работать. Я думаю, что эта страница эквивалентна: github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/   -  person RonLugge    schedule 07.06.2018


Ответы (3)


Одноразовое решение

Обычные сериализаторы работают только с отдельными элементами, а не со списками с разбивкой на страницы. Самый простой способ добавить разбиение на страницы — в контроллере:

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 в качестве пагинатора. Тем не менее, его можно легко изменить для работы с любым жемчугом разбивки на страницы или пользовательским решением.

person lightswitch05    schedule 11.04.2014
comment
Это довольно утомительный и повторяющийся способ сделать это, если у вас много ресурсов, которые вы хотите разбить на страницы. - person Mark Murphy; 23.04.2014
comment
@MarkMurphy Вы правы - очень утомительно. Я нахожусь в процессе перемещения деталей разбивки на страницы в собственный сериализатор и обновлю свой ответ, как только устраню оставшиеся ошибки. А пока вы можете искать ArraySerializer - person lightswitch05; 23.04.2014
comment
@MarkMurphy Я обновил свой ответ, чтобы показать, как использовать ArraySerializer для очистки кода и быть более СУХИМ - person lightswitch05; 13.06.2014
comment
Я хотел, чтобы ключи разбиения на страницы были верхнего уровня, поэтому я также перезаписал #as_json на PagedSerializer < ActiveModel::ArraySerializer 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 - person aaronmgdr; 18.12.2014
comment
Спасибо за такое хорошее решение, но кажется, что ActiveModel::Serializer::ArraySerializer должно быть ActiveModel::ArraySerializer. - person Li Dong; 02.12.2015
comment
@LiDong, это зависит от того, какая у вас версия active_model_serializers. 0.10.0 и новее требует ActiveModel::Serializer::ArraySerializer - более старые версии требуют ActiveModel::ArraySerializer - person lightswitch05; 02.12.2015
comment
@ lightswitch05 Да, вы правы, я использую 0.9.3. - person Li Dong; 03.12.2015
comment
Отличное решение, но больше не требуется, см.: github.com /rails-api/active_model_serializers/blob/master/docs/ - person rmcsharry; 08.09.2016
comment
Как добавить измененный URL-адрес документа со ссылками на страницы в github .com/rails-api/active_model_serializers/blob/v0.10.6/docs/ - person Zoran Majstorovic; 18.10.2017
comment
Начиная с версии 0.10, эта стратегия больше не будет работать из-за того, что мета-параметры перемещаются в адаптер (т. е. мета-параметры не обрабатываются CollectionSerializer). Таким образом, аналогичный подход можно использовать, создав адаптер (например, адаптер JsonApi, предоставляемый библиотекой, делает ссылки автоматически, если ваш ресурс их поддерживает) - person aromero; 31.12.2017

Обновление 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_api или json. Проверьте, какой из них вы используете на ActiveModelSerializers.config.adapter.

Если вы используете адаптер JSON API (ваш ActiveModelSerializers.config.adapter = :json_api)

Ссылки на страницы будут включены в ваш ответ автоматически, если ресурс разбит на страницы и если вы используете адаптер JsonApi.

Если вы хотите, чтобы в вашем ответе были ссылки на страницы, используйте Kaminari или WillPaginate.

Kaminari examples
#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
WillPaginate examples
#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 (ваш ActiveModelSerializers.config.adapter = :json)

Если вы не используете адаптер 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, из-за чего невозможно добавить ссылки на страницы.

person sandre89    schedule 18.04.2020
comment
Ключ здесь ActiveModelSerializers.config.adapter = :json_api в файле в вашем config/initializers/ams.rb. - person Gregory Ray; 27.08.2020

https://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)

полный исходный код https://github.com/x1wins/tutorial-rails-rest-api< /а>

person Changwoo Rhee    schedule 30.01.2020