Используйте active_model_serializer с объектом, отличным от ActiveRecord.

У меня есть объект модели, который не является потомком ActiveRecord::Base и не хранится в базе данных. Я создал для него сериализатор (с таким же названием + "Сериализатор"), а в своем контроллере вызываю render json: object_instance.

Результатом является исключение из глубины render.

Я реализовал метод as_json, который создает экземпляр сериализатора и вызывает его, но в результате в объекте отсутствует метод read_attribute_for_serialization.

Я хочу, чтобы мой объект действовал как объект, совместимый с ActiveModel, по крайней мере, в том, что касается Active Model Serializer, но я не вижу никаких ссылок на это в их документации.


person Robin Daugherty    schedule 11.02.2014    source источник


Ответы (6)


Объект модели должен включать его таким образом:

# active_model_serializers 0.10+
class ModelName
  include ActiveModel::Serialization
end

# active_model_serializers < 0.10
class ModelName
  include ActiveModel::SerializerSupport
end

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

Это работает с версиями active_model_serializers с 0.8.1 по крайней мере до 0.9.3.

person Robin Daugherty    schedule 11.02.2014
comment
Привет, Робин, можешь привести пример своего объекта для людей, которые хотят сделать что-то подобное с нуля? (это неактивный объект записи, который включает поддержку сериализатора) - person Will; 15.05.2015
comment
вам удалось сериализовать массив вложенных сериализованных объектов внутри модели? - person Chris; 13.11.2015
comment
Крис, можешь открыть новый вопрос для этого и включить свой код? Я мог бы ответить на него, но мне нужно больше деталей. - person Robin Daugherty; 14.11.2015

Следуя ответу mutexkid, с active_model_serializers 0.10.0.rc4 требуется следующее, чтобы простой старый рубиновый объект хорошо работал с сериализатором:

ПОРО:

class SearchResult
  include ActiveModel::Serialization

  attr_reader :stories, :users, :friends

  def initialize(search:)
    @stories = search.stories
    @users = search.users
    @friends = search.friends
  end
end

Сериализатор:

class SearchResultSerializer < ActiveModel::Serializer
  attributes :stories, :users, :friends
end
person b73    schedule 11.03.2016
comment
Это использовало Rails 4.0.13 - person b73; 11.03.2016
comment
Это также задокументировано (может быть более очевидным) и включено в пакет 0.10. серия: github. com/rails-api/active_model_serializers/blob/ github.com/rails-api/active_model_serializers/blob/ - person BF4; 21.03.2016
comment
Прекрасно работает с Rails 5.0.1 и AMS 0.10.4. - person Brian Sigafoos; 24.02.2017

Я столкнулся с этим вчера при обновлении до Rails 4.2. ActiveModel::SerializerSupport был удален для стабильной версии 0.10. В итоге я просто добавил псевдоним к своим PORO, что, похоже, помогло:

  alias :read_attribute_for_serialization :send

(active_model_serializers 0.10.0.rc2)

Вам также нужно будет добавить include ActiveModel::Model в свой класс.

person Karl Wilbur    schedule 11.08.2015

начиная с active_model_serializers 0.10.x и Rails 4.2, вот что требуется для использования простого старого рубинового объекта с активными сериализаторами модели:

Обычный старый рубиновый объект:

class SearchResult
  extend ActiveModel::Naming
  include ActiveModel::Serialization
  attr_accessor :stories, :users, :friends

  def id
    hash 
  end

  def initialize(search:)
    @stories = search.stories
    @users = search.users
    @friends = search.friends
  end

  def attributes
    { stories: stories,
      users: users,
      friends: friends
    }
  end
end

Сериализатор:

class SearchResultSerializer < ActiveModel::Serializer
  attributes :stories, :users, :friends
end
person mutexkid    schedule 13.01.2016
comment
с `active_model_serializers 0.10.0.rc4: я нашел большую часть этой настройки ненужной, см. мой ответ ниже. Я добавил отдельный ответ, так как комментарии не слишком хорошо обрабатывают блоки кода: - person b73; 11.03.2016
comment
Это решение работало для меня в Rails 5.0.6 - 1) расширить ActiveModel::Naming 2) включить ActiveModel::Serialization 3) реализовать def attributes - person Peter P.; 12.06.2018

Моя учетная запись слишком новая, чтобы комментировать ответ Карла Уилбура, но вот код, который я добавил, чтобы я мог передать хэш-объект для сериализации:

class SerializableHash < Hash
  alias :read_attribute_for_serialization :[]
end

Применение:

  o = SerializableHash.new(user: current_user, account: current_account)
  respond_with(o, serializer: SettingsSerializer)
person ratbeard    schedule 03.09.2015

В дополнение к ответу @ratbeard следующее работает с любой версией active_model_serializer 0.9.x и выше. Я включил минимальное количество классов active_support и active_model, необходимых для того, чтобы сериализуемый хеш-объект PORO работал с сериализаторами ActiveModel вне рельсов. В моих целях в качестве оптимизации я передаю предварительно вычисленный сериализованный массив объектов, который вычисляется в другом месте приложения. Вместо повторного вычисления вы можете перегрузить инициализатор в подклассе ActiveModel::Serializer, чтобы передать его, если он доступен.

lib/serializable_hash.rb

class SerializableHash < Hash
  alias :read_attribute_for_serialization :[]
end

app/serializers/email/foo_serializer.rb

require 'active_model/serializer'
require 'active_support/core_ext/object'
require 'active_support/inflector'
require 'active_support/notifications'
class Email::FooSerializer < ActiveModel::Serializer
  attributes :id, :bars, :created_at, :updated_at

  def initialize(foo, precomputed_serialized_array_of_bars =nil)
    unless serialized_bars
      serialized_bars = {}
      foo.bar.each do | bar |
        serialized_searches[bar.baz] ||= []
        serialized_searches[bar.baz] << Email::BarSerializer.new(bar).attributes
      end
    end

    super(SerializableHash[
      id: foo.id,
      bars: serialized_bars,
      created_at: foo.created_at
      updated_at: foo.updated_at
    ])
  end
end

Для полноты я также включил эту альтернативную версию, в которой вместо сериализуемого хэша используется переменная экземпляра:

app/serializers/email/foo_serializer.rb

require 'active_model/serializer'
require 'active_support/core_ext/object'
require 'active_support/inflector'
require 'active_support/notifications'
class Email::FooSerializer < ActiveModel::Serializer
  attributes :id, :bars, :created_at, :updated_at

  def initialize(foo, precomputed_serialized_array_of_bars=nil)
    @bars = precomputed_serialized_array_of_bars
    super(foo)
  end

  def bars
    if @bars
      return @bars
    else
      serialized_bars = {}
      object.bars.each do | bar |
        serialized_bars[bar.baz] ||= []
        serialized_bars[bar.baz] << Email::BarSerializer.new(bar).attributes
      end
      return serialized_bars
    end
  end
end

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

require 'app/serializers/email/foo_serializer'
require 'app/serializers/email/bar_serializer'
Email::FooSerializer.new(Foo.first).as_json

ИЛИ, если вы хотите использовать предварительно вычисленный сериализованный массив баров:

# pre-compute array_of_serialized_bars somewhere up here

require 'app/serializers/email/foo_serializer'
require 'app/serializers/email/bar_serializer'
Email::FooSerializer.new(Foo.first, array_of_pre_serialized_bars).as_json
person Jesse Sanford    schedule 03.12.2015
comment
class SerializableHash < Hash alias :read_attribute_for_serialization :[] end Это действительно плохая идея по разным причинам. Просто. Не. Наследование основных типов в MRI имеет неожиданное поведение. Я хотел бы написать больше. - person BF4; 21.03.2016