Сериализаторы активных моделей: параллельные коллекции для эффективных запросов?

Допустим, у меня есть несколько моделей: User, Place и Review. Review — это, по сути, пересечение между User и Place, и belongs_to каждый.

Мы возвращаем несколько разных конечных точек, сосредоточенных вокруг коллекций мест, с прикрепленными отзывами.

Чтобы соответствовать другим конечным точкам, возвращающим места с прикрепленными отзывами, я пытаюсь создать ленту последних созданных обзоров, например:

[
  place: 'Pizza Pie',
    review: 'latest review!',
  place: 'Snuffleupagus' Shack',
    review: 'second latest review',
  etc. etc.
]

Вот мое действие контроллера (expose взято из RocketPants; оно автоматически вызывает serializable_hash):

def feed
  review_ids = Review.order("updated_at DESC").pluck(:id)
  expose Place.joins(:reviews).where(reviews: { id: review_ids }).order("reviews.updated_at DESC"), each_serializer: PlaceFeedSerializer
end

Он вернет массив мест и сериализует каждое из них через следующие PlaceFeedSerializer:

class PlaceFeedSerializer < ActiveModel::Serializer
  attributes :name, :address # etc. etc.
  has_one :review, serializer: ReviewSerializer
  def review
    object.reviews.order("updated_at DESC").first
  end
end

Для этого я делаю два предварительных запроса ActiveRecord, а затем новый запрос для каждого сериализованного обзора. Это кажется хакерским и медленным.

Есть ли способ в контроллере передать два массива, которые будут сериализованы параллельно? Или я могу создать ActiveModel::ArraySerializer, который будет принимать соответствующие массивы places и reviews?

Или я перегружаю простотой ActiveModel::Serializer, когда мне было бы лучше использовать что-то вроде JBuilder?

Возможно, другим вариантом, если это возможно, будет сериализация каждого обзора, «завернутого» в связанное с ним место?

Заранее спасибо!


person Aron    schedule 02.05.2014    source источник


Ответы (2)


Не по теме, но вы можете рассмотреть возможность использования :include вместо :joins, чтобы быстро загрузить информацию Place с информацией Review. Соответствующие рельсовые трансляции здесь.

person Jonny Appleseed    schedule 07.05.2014

Я придумал довольно хакерский способ сделать это, но, похоже, он работает.

Передача массива Reviews из контроллера с использованием each_serializer: PlaceFeedSerializer будет выглядеть примерно так:

class PlaceFeedSerializer < ActiveModel::Serializer
  delegate :current_resource_owner, to: :scope

  def initialize(*args)
    super
    @place = object.place
  end

  # Re-created association (need to manually attach in 'attributes')
  def review
    ReviewSerializer.new(object, @options)
  end

  # NOTE: This retrieves ONLY the attributes from PlaceSerializer
  #       so we need to create the associations manually (not DRY!)
  def attributes
    hash = PlaceSerializer.new(@place, @options).attributes
    hash[:review] = review
    hash
  end
end

Если у кого-то есть другие идеи, не стесняйтесь вносить свой вклад! Спасибо.

person Aron    schedule 07.05.2014