Active Model Serializers 中的条件属性

54

如何只在满足一些条件时才渲染属性?

例如,在创建操作中,我想要渲染用户的token属性。


你可以覆盖attributes方法。 - apneadiving
@apneadiving,有没有更简单、更清晰的方法来做这件事? - Dan
6个回答

86
在最新版本(0.10.x)中,您也可以这样做:
class EntitySerializer < ActiveModel::Serializer
  attributes :id, :created_at, :updated_at
  attribute :conditional_attr, if: :condition?

  def condition?
    #condition code goes here
  end
end

例如:

class UserSerializer < ActiveModel::Serializer
  attributes :id, :username, :name, :email, :created_at, :updated_at
  attribute :auth_token, if: :auth_token?

  def created_at
    object.created_at.to_i
  end

  def updated_at
    object.updated_at.to_i
  end

  def auth_token?
    true if object.auth_token
  end
end

编辑(由Joe Essey建议):

这种方法不适用于最新版本(0.10

在版本0.8中,它甚至更简单。您不必使用if: :condition?。而是可以使用以下约定来实现相同的结果。

class EntitySerializer < ActiveModel::Serializer
  attributes :id, :created_at, :updated_at
  attribute :conditional_attr

  def include_conditional_attr?
    #condition code goes here
  end
end

上面的例子看起来像这样。
class UserSerializer < ActiveModel::Serializer
  attributes :id, :username, :name, :email, :created_at, :updated_at
  attribute :auth_token

  def created_at
    object.created_at.to_i
  end

  def updated_at
    object.updated_at.to_i
  end

  def include_auth_token?
    true if object.auth_token
  end
end

查看0.8文档获取更多详情。


2
该库已更新。您不再需要使用 attribute :foo, if: :bar。只需为要有条件显示的每个属性命名一个函数,例如:include_foo?,并在那里进行布尔检查即可。这些文档已经链接在答案中了。 - Joe Essey
6
自0.10版本起,include_foo?方法似乎不再有效。 - RonLugge
3
抱歉,明确一下,自动的 include_foo? 方法似乎已被删除 -- 你仍然可以向属性方法添加一个 if: foo? 参数。 - RonLugge
如果能更新一下这个答案就好了,自动包含方法在新版本中已经不起作用了。 - Joel Blum
@JoelBlum 这段时间我都没有用 RoR,所以不太了解新版本。你可以直接更新。 - Abdelhakim AKODADI
显示剩余4条评论

52

您可以覆盖attributes方法,以下是一个简单的示例:

class Foo < ActiveModel::Serializer

  attributes :id

  def attributes(*args)
    hash = super
    hash[:last_name] = 'Bob' unless object.persisted?
    hash
  end

end

1
这对于添加动态属性非常棒。 - bennick
我绝对更喜欢这种方法。它使添加大量条件语句更加简洁,因为您不必为每个属性都编写一个方法(这些方法非常冗余)。 - Chris Cirefice

5
您可以从序列化程序的“initialize”方法上设置条件开始。这个条件可以从您代码中的任何其他位置传递,包含在选项哈希中,作为“initialize”接受的第二个参数:
class SomeCustomSerializer < ActiveModel::Serializer

  attributes :id, :attr1, :conditional_attr2, :conditional_attr2

  def initialize(object, options={})
    @condition = options[:condition].present? && options[:condition]
    super(object, options)
  end

  def attributes(*args)
    return super unless @condition  #get all the attributes
    attributes_to_remove = [:conditional_attr2, :conditional_attr2]
    filtered = super.except(*attributes_to_remove)
    filtered
  end
end

在这种情况下,attr1将始终被传递,而如果条件为真,则条件属性将被隐藏。
您可以在代码的其他位置按以下方式获取此自定义序列化的结果:
custom_serialized_object = SomeCustomSerializer.new(object_to_serialize, {:condition => true})

我希望您能从中受益!

2
序列化器 options 已经合并到 ActiveModel Serializers 中,现在可用(自 0.10 版本起)。

3
很遗憾,您链接的讨论没有提供任何有用的说明在哪里找到这些选项或如何使用它们。 - RonLugge

1

覆盖是个不错的想法,但如果你使用super,属性将在你移除想要的东西之前被计算。如果这对你没有影响,那没问题,但当有影响时,你可以使用它:

def attributes(options={})
  attributes =
    if options[:fields]
      self.class._attributes & options[:fields]
    else
      self.class._attributes.dup
    end

  attributes.delete_if {|attr| attr == :attribute_name } if condition

  attributes.each_with_object({}) do |name, hash|
    unless self.class._fragmented
      hash[name] = send(name)
    else
      hash[name] = self.class._fragmented.public_send(name)
    end
  end
end

ps: v0.10.0.rc3


1
这里是如何直接向序列化器实例传递参数并在序列化器声明中基于这些参数显示或隐藏属性的方法。
它也适用于父子序列化器。
控制器或父序列化器:
ActiveModelSerializers::SerializableResource.new(object.locations, {
  each_serializer: PublicLocationSerializer,
  params: { 
    show_title: true
  },
})

带有条件的序列化器:

class PublicLocationSerializer < ActiveModel::Serializer
  attributes :id, :latitude, :longitude, :title

  def title
    object.title if @instance_options[:params][:show_title]
  end

end

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