如何使用ActiveRecord::Relation数组初始化ActiveModel::Serializer类?

48

我有一个序列化器

class FundingSerializer < ActiveModel::Serializer
  attributes :id, 

  has_one :user
  has_one :tournament
  embed :ids, include: true
end

以适当的关联进行初始化

FundingSerializer.new(Funding.first).to_json
产生收益
"{\"users\":[{\"id\":2,\"first_name\":\"Nick\"}],\"tournaments\":[{\"id\":1,\"end_date\":\"2013-07-21T23:18:54.981Z\",\"start_date\":\"2013-07-14T23:18:54.980Z\"}],\"funding\":{\"id\":1}}"

但是,

FundingSerializer.new(Funding.all).to_json

出现了这个错误。

undefined method `read_attribute_for_serialization' for #<ActiveRecord::Relation::ActiveRecord_Relation_Funding:0x007f998910a250>
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activerecord-4.0.0/lib/active_record/relation/delegation.rb:121:in `method_missing'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activerecord-4.0.0/lib/active_record/relation/delegation.rb:68:in `method_missing'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:99:in `block in attribute'
    from (eval):3:in `_fast_attributes'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:466:in `rescue in attributes'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:454:in `attributes'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:478:in `_serializable_hash'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:360:in `serializable_hash'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:344:in `as_json'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/json/encoding.rb:50:in `block in encode'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/json/encoding.rb:81:in `check_for_circular_references'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/json/encoding.rb:49:in `encode'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/json/encoding.rb:34:in `encode'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/core_ext/object/to_json.rb:16:in `to_json'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:333:in `to_json'
    from (irb):1
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/railties-4.0.0/lib/rails/commands/console.rb:90:in `start'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/railties-4.0.0/lib/rails/commands/console.rb:9:in `start'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/railties-4.0.0/lib/rails/commands.rb:64:in `<top (required)>'

我不仅想要简单地渲染json:Funding.all,因为我希望将这个json传递给我的rails应用程序中的其他对象和一个angularjs应用程序。谢谢。


虽然SandOnTeeth的回答可能有效,但这不是官方API。请参考文档 - Alexander Popov
8个回答

177

我想这就是你在寻找的内容:

ActiveModel::ArraySerializer.new(Funding.all, each_serializer: FundingSerializer).to_json

查看这个Thoughtbot博客文章,以获取示例。


太棒了,正是我所需要的。这应该放在 AMS 维基上的 Github 上。 - Matjaz Muhic
你是否在你的Gemfile中添加了gem "active_model_serializers" - John
17
这不适用于"active_model_serializers" gem >=0.10.0。::ActiveModel::ArraySerializer已经被移动到::ActiveModel::Serializer::ArraySerializer,并且类的结构已经发生了重大变化。 - Zeke Fast
7
如果有人通过谷歌搜索最新的AMS主控(截至2016年4月)并发现自己遇到了这个问题,@ZekeFast提醒你要记住使用ActiveModel :: Serializer :: CollectionSerializer而不是其他类,因为AMS团队很快将弃用另一个类。ActiveModel::Serializer::CollectionSerializer.new(object.myrelation, each_serializer: MySerializer) - Alfredo Gallegos
6
对我而言,ActiveModel::Serializer::CollectionSerializer.new(object.myrelation, serializer: MySerializer) 起到了作用(each_serializer 选项被忽略了)。 - biomancer
显示剩余6条评论

28
我不确定这是否是一个惯用的解决方案,但它应该可以运行:
Funding.all.map{|f| FundingSerializer.new(f)}.to_json

2
那确实可行,我只是认为会有类似于ActiveModel :: ArraySerializer.new的解决方案... - shicholas
1
ActiveModel::ArraySerializer 的内部,基本上是这个确切的实现。对集合进行映射并返回序列化实例。 - Blair Anderson
3
在 "active_model_serializers" 版本号大于等于 0.10.0 上无法正常工作。 - Zeke Fast

11
这对我来说在版本0.10.2中有效: ActiveModelSerializers::SerializableResource.new(Funding.all, each_serializer: FundingSerializer).to_json

1
ActiveModelSerializers::SerializableResource 是当前官方的 API,截至版本 0.10.2,这个答案是正确的。 - Alexander Popov
确认这是目前唯一正确的答案。 - Trip

8
现在你可以通过以下方式来实现(使用 AMS v0.10.2):
ActiveModel::Serializer.serializer_for(Funding.all).to_json

编辑(2017年3月3日)
此方法不再有效。请使用aNoble的答案:

ActiveModelSerializers::SerializableResource.new(Funding.all).to_json

2
从版本0.10.2开始,aNoble提供了正确的答案。此外,请查看有关此主题的文档 - Alexander Popov
我正在使用AMS 0.10.2版本,ActiveModel :: Serializer.serializer_for(Leaderboard.all)。to_json,即使有十几个Leaderboard记录,它始终返回“{ }”。 - Muhammad Faisal Iqbal
@MuhammadFaisalIqbal,请检查我的更新答案。 - Andrei

6

看起来在最新的ActiveModel::Serializers gem版本中,他们再次进行了调整。您不再可以在ArraySerializer(不是ActiveModel::Serializer::ArraySerializer)上调用to_json。

以下是我为解决此问题所做的操作。

let(:resource)        { Funding.all }
let(:serializer)      { ActiveModel::Serializer::ArraySerializer.new(resource, each_serializer: FundingSerializer)
let(:serialization)   { ActiveModel::Serializer::Adapter.create(serializer) }
subject               { JSON.parse(serialization.to_json) }

调用主题会得到您想要的JSON数据。以下是一些更多的资源,可以进一步探讨有关测试设置的内容: http://eclips3.net/2015/01/24/testing-active-model-serializer-with-rspec/ https://robots.thoughtbot.com/validating-json-schemas-with-an-rspec-matcher

6
你也可以明确地提供集合序列化器。
render json: Funding.all, serializer: ActiveModel::ArraySerializer, each_serializer: FundingSerializer

这是正确的答案,至少对于版本<0.10。您可以将each_serializer作为一个选项传递进去。 - quainjn
当我移除序列化器:ActiveModel::ArraySerializer时,它对我有效。 - Piotr Galas

3

我用RSpec做了一个简单的帮助程序来测试版本号为>= 0.10.0active_model_serializers响应,代码如下:

module AMSHelper
  def ams_array_serializer(collection, options: {}, adapter: :json)
    serializer = ActiveModel::Serializer::ArraySerializer.new(collection)
    adapter_class = ActiveModel::Serializer::Adapter.adapter_class(adapter)
    adapter_class.new(serializer, options)
  end

  def ams_serializer(object, options: {}, adapter: :json)
    serializer = ActiveModel::Serializer.serializer_for(object).new(object)
    adapter_class = ActiveModel::Serializer::Adapter.adapter_class(adapter)

    adapter_class.new(serializer, options)
  end
end

RSpec.configure do |config|
  config.include AMSHelper, type: :request
end

因此,您可以使用以下方法进行测试:

RSpec.describe "Posts", type: :request do
  describe "GET /" do
    it "returns http success" do
      get posts_path
      expect(response_body).to eq(ams_array_serializer(Post.all).to_json)
    end
  end
end

我希望这对某些人有用。

ams_serializer 工作正常,但 ams_array_serializer 不起作用。https://dev59.com/X1oV5IYBdhLWcg3wauOP - Vipin Verma

1
假设您有一个序列化器类(Foo),但其名称与资源名称(Bar)不匹配,我使用以下方法轻松地对对象进行序列化:
class BaseSerializer < ActiveModel::Serializer
  def self.collection_serialize(resources)
    ActiveModelSerializers::SerializableResource.new(resources, each_serializer: self)
  end
end

让Foo序列化器继承BaseSerializer:
class FooSerializer < BaseSerializer
  ...
end

在控制器内或外部使用FooSerializer:

bar = Bar.all
FooSerializer.collection_serialize(bar).to_json

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接