Rails 4 рендерит json с несколькими объектами и включает в себя - PullRequest
0 голосов
/ 26 сентября 2018

У меня есть Rails 4 API.Когда пользователь выполняет поиск в представлении для лодок, этот метод выполняется, получая все лодки, соответствующие поисковым фильтрам, и возвращает массив моделей лодок в виде json, используя рендер ActiveModel и: include и: only like:

render :json => @boats, :include => { :mainPhoto => {:only => [:name, :mime]},
                                      :year => {:only => :name},
                                      # other includes...}

Это прекрасно работает.

Но, в дополнение к этой информации, я хотел бы показать общее количество лодок, таких как "показ 1 - 20 из 80 лодок", потому что существует функция пагинации.Итак, дело в том, что мне нужно предоставить лодки 80 .Я хотел бы избежать отправки двух запросов, которые выполняют почти одинаковую логику, поэтому идея состоит в том, чтобы запустить метод searchBoats только один раз, и в результате предоставить список лодок и общее количество лодок в переменной numTotalBoats.Я понимаю, что numTotalBoats не является атрибутом модели лодки.Итак, я думаю, что это должно идти в независимой переменной в результате рендеринга.Что-то вроде:

render :json => {boats: @boats with all the includes, numTotalBoats: @NumTotalBoats}

Я пробовал тысячи комбинаций, но или я получаю синтаксические ошибки, или ни одна из них не возвращает ожидаемый результат, что-то вроде

{boats: [boat1 with all the includes, boat2 with all the includes, ... boatN with all the includes], numTotalBoats: N}

1 Ответ

0 голосов
/ 28 сентября 2018

Без добавления каких-либо драгоценных камней:

def index
  boats = Boat.includes(:year)

  render json: {
    boats: boats.as_json(include: { year: { only: :name } }),
    numTotalBoats: boats.count
  }
end

В какой-то момент я считаю, что вам следует использовать автономные сериализаторы:

Примечание : в зависимости от того,Если вы используете гем нумерации страниц или нет, вам может потребоваться изменить .count вызовов ниже на .total_count (для Каминари) или что-то еще, что будет правильно считывать счет из нумерации страниц.

Я рекомендую использовать Сериализаторы ActiveModel и вот как это будет сделано для вашего случая.

Начните с добавления камня в Gemfile:

gem 'active_model_serializers', '~-> 0.10'

Перезапишите адаптер в config / initializers / active_model_serializer.rb:

ActiveModelSerializers.config.adapter = :json

Определите сериализаторы для ваших моделей,

# app/serializers/boat_serializer.rb
class BoatSerializer < ActiveModel::Serializer
  attributes :name

  has_one :year
end

# app/serializers/year_serializer.rb
class YearSerializer < ActiveModel::Serializer
  attributes :name
end

И, наконец, в вашем контроллере:

boats = Boat.includes(:year)

render json: boats, meta: boats.count, meta_key: "numTotalBoats"

И вы достигнете:

{
  "boats": [
    {
      "name": "Boaty McBoatFace",
      "year": {
        "name": "2018"
      }
    },
    {
      "name": "Titanic",
      "year": {
        "name": "1911"
      }
    }
  ],
  "numTotalBoats": 2
}

Добавление количества в каждом контроллере индекса немного утомительнотак что я обычно заканчиваю тем, что определяю свои собственные адаптеры или сериализаторы коллекций, чтобы позаботиться об этом автоматически (протестировано с Rails 5, а не 4).

# lib/active_model_serializers/adapter/json_extended.rb
module ActiveModelSerializers
  module Adapter
    class JsonExtended < Json
      def meta
        if serializer.object.is_a?(ActiveRecord::Relation)
          { total_count: serializer.object.count }
        end.to_h.merge(instance_options.fetch(:meta, {})).presence
      end
    end
  end
end

# config/initializers/active_model_serializer.rb
ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonExtended

# make sure to add lib to eager load paths
# config/application.rb
config.eager_load_paths << Rails.root.join("lib")

И теперь ваше действие индекса может выглядеть следующим образом

def index
  boats = Boat.includes(:year)

  render json: boats
end

И вывод:

{
  "boats": [
    {
      "name": "Boaty McBoatFace",
      "year": {
        "name": "2018"
      }
    },
    {
      "name": "Titanic",
      "year": {
        "name": "1911"
      }
    }
  ],
  "meta": {
    "total_count": 2
  }
}

Я думаю, что немного проще проанализировать этот счетчик для разных конечных точек, и вы получите его автоматически при ответе с коллекцией, так что ваши контроллеры будутнемного проще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...