如何使用ActiveModel::Serializers为同一模型实现多个不同的序列化器?

47

假设你正在Rails中实现一个REST API。在提供集合时,可能只想包含少量属性:

/people

但是当提供单个资源时,您想要包括所有属性:

/people/1

我不知道如何使用ActiveModel::Serializers实现这一点,因为所有的例子都使用定义一个模型对应一个序列化器的模式(有一个标准的命名约定),并且在控制器中使用AMS自动使用正确的序列化器。

render json: @people

或者:

render json: @person
5个回答

130

您可以为同一模型拥有多个序列化器,例如:

class SimplePersonSerializer < ActiveModel::Serializer
  attributes :id, :name
end

class CompletePersonSerializer < ActiveModel::Serializer
  attributes :id, :name, :phone, :email
end

一个控制器中的简单信息:

render json: @people, each_serializer: SimplePersonSerializer

其他地区的人完整信息:

render json: @people, each_serializer: CompletePersonSerializer

一个人的简单信息:

render json: @person, serializer: SimplePersonSerializer

一个单个人的完整信息:

render json: @person, serializer: CompletePersonSerializer

2
@SeifSallam,你解决了这个问题吗?我也遇到了同样的问题。 - chopper
9
是的,你只需要添加 root: :person。以下是完整的示例:render json: @person, serializer: CompletePersonSerializer, root: :person - Seif Sallam
3
您可以通过继承 SimplePersonSerializer 类来创建一个子类 CompletePersonSerializer,并添加您需要的新属性。 - Mark Swardstrom
3
你可以在序列化类本身中添加 root: person。这样你在调用 render json 的时候就不需要到处指定它了。 - Ranhiru Jude Cooray
2
Phaedryx和@Swards,你们是我的英雄! - Chris Cirefice
显示剩余3条评论

11
class CompletePersonSerializer < ActiveModel::Serializer
  root :person
  attributes :id, :name, :phone, :email
end
或者
render json: @people, each_serializer: CompletePersonSerializer, root: :person

截至今日,root:person 报错。each_serializer: CompletePersonSerializer 足够。 - nerfologist

4
为了避免将视图问题(通过序列化变量)混入您的模型中,请使用视图为每个操作呈现JSON,就像我们为HTML做的那样。 jbuilderrabl都非常好地填补了这种数据模板需求。 更新2013-12-16: ActiveModelSerializers库支持为一个模型定义多个序列化器,正如@phaedryx后来回答的那样,使用自定义序列化器

2
所以,你的意思是AMS在当前设计中还没有足够的灵活性来满足不同API响应的不同序列化的常见用例?因为这就是它的感觉,但我不想假设问题不仅仅是我的无知,特别是因为AMS被认为是Rails4核心的“方法”... - odigity
1
jbuilder是由DHH专门为解决Rails中的这个问题而创建的。 通过ActiveModel::Serializers创建变体最终会将公共API(视图)关注点放在您的控制器和/或模型中。 - Mars
好的。显然我需要看一下jbuilder,然后决定如何继续,但这是一个很好的答案。谢谢! - odigity

2

接着 @phaedryx 的说法,我为此调用一个返回正确序列化程序的方法...对于你的问题,我会使用:

class MyController < ApplicationController

  def index
    render json: @people, each_serializer: serializer_method
  end

  private

  def serializer_method
    defined?(@people) ? PeopleSerializer : PersonSerializer
  end

end

0

我认为最好为每个控制器操作都有一个特定的序列化程序。我构建了这个关注点来处理它。(我将其添加到基本控制器中)

module Serializable
  extend ActiveSupport::Concern

   alias each_serializer serializer

   def serializer
     "#{params[:controller].classify}s::#{params[:action].classify}Serializer".constantize
   end
end

这样你就可以直接调用了

render json: @person, serializer:

在您的控制器中,它将会找到正确的序列化器。


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