如何根据特定字段限制 active_model_serializers 属性,而不必在每个属性上放置 `if:` 选项?
How to limit the active_model_serializers attributes depending on a specific field without having to put the `if:` option on every attribute?
我有一个模型可以有各种类型,这些类型会影响正在使用的字段。
我想从输出中删除未使用的字段。目前,这有效:
attribute :field1, if: :is_type1?
attribute :field2, if: :is_type1?
attribute :field3, if: :is_type2?
attribute :field4 # always shown
问题是我有很多属性,这可能会变得乏味且难以维护,尤其是当一个字段可以被某些但不是所有类型使用时。我知道也许可以将模型化更改为具有多个模型,从而具有多个序列化程序,但这不是我现在负担不起的,所以我正在寻找另一种解决方案。
我设想了两种方法:要么使用某种块来定义属性,例如:
given -> { object.type == 1 } do
attributes :field1, :field2
end
given -> { object.type == 2 } do
attributes :field3
end
attribute :field4
或者在 if:
中到处使用一些通用方法。就此而言,我已经在我的模型中定义了适用于每种类型的字段:
def allowed_attr
allowed_attr = {
field1: false,
field2: false,
field3: false
}
case type
when 1
allowed_attr[:field1] = true
allowed_attr[:field2] = true
when 2
allowed_attr[:field3] = true
end
allowed_attr
end
我想在我的序列化程序中做的是这样的事情:
attribute :field1, if: -> { allowed(attribute_name) }
attribute :field2, if: -> { allowed(attribute_name) }
attribute :field3, if: -> { allowed(attribute_name) }
attribute :field4, if: -> { allowed(attribute_name) }
def allowed(attr)
object.allowed_attr[attr] != false # allows everything that is not precisely set to false
end
问题是我似乎无法访问当前正在测试的字段 (attribute_name
)
有什么方法可以完成我希望我忽略的事情吗?
编辑
这是我根据 Omnigazer 的回答使用的解决方案。
[
:field1
[:field1_attr, :field1],
[:field2, Proc.new { object.field1.present? }]
].each do |attr|
if attr.kind_of? Array
name = attr.shift
if attr[0].kind_of? Symbol
allowed = attr.shift
end
if attr[0].kind_of? Proc
cond = attr.shift
end
end
name ||= attr
allowed ||= name
cond ||= Proc.new { true }
attribute name, if: -> { instance_eval(&cond) && object.allowed_attribute?(allowed) }
end
你可以这样做:
[:field1, :field2, :field3, :field4].each do |attribute_name|
attribute attribute_name, if: -> { allowed(attribute_name) }
end
我有一个模型可以有各种类型,这些类型会影响正在使用的字段。 我想从输出中删除未使用的字段。目前,这有效:
attribute :field1, if: :is_type1?
attribute :field2, if: :is_type1?
attribute :field3, if: :is_type2?
attribute :field4 # always shown
问题是我有很多属性,这可能会变得乏味且难以维护,尤其是当一个字段可以被某些但不是所有类型使用时。我知道也许可以将模型化更改为具有多个模型,从而具有多个序列化程序,但这不是我现在负担不起的,所以我正在寻找另一种解决方案。
我设想了两种方法:要么使用某种块来定义属性,例如:
given -> { object.type == 1 } do
attributes :field1, :field2
end
given -> { object.type == 2 } do
attributes :field3
end
attribute :field4
或者在 if:
中到处使用一些通用方法。就此而言,我已经在我的模型中定义了适用于每种类型的字段:
def allowed_attr
allowed_attr = {
field1: false,
field2: false,
field3: false
}
case type
when 1
allowed_attr[:field1] = true
allowed_attr[:field2] = true
when 2
allowed_attr[:field3] = true
end
allowed_attr
end
我想在我的序列化程序中做的是这样的事情:
attribute :field1, if: -> { allowed(attribute_name) }
attribute :field2, if: -> { allowed(attribute_name) }
attribute :field3, if: -> { allowed(attribute_name) }
attribute :field4, if: -> { allowed(attribute_name) }
def allowed(attr)
object.allowed_attr[attr] != false # allows everything that is not precisely set to false
end
问题是我似乎无法访问当前正在测试的字段 (attribute_name
)
有什么方法可以完成我希望我忽略的事情吗?
编辑
这是我根据 Omnigazer 的回答使用的解决方案。
[
:field1
[:field1_attr, :field1],
[:field2, Proc.new { object.field1.present? }]
].each do |attr|
if attr.kind_of? Array
name = attr.shift
if attr[0].kind_of? Symbol
allowed = attr.shift
end
if attr[0].kind_of? Proc
cond = attr.shift
end
end
name ||= attr
allowed ||= name
cond ||= Proc.new { true }
attribute name, if: -> { instance_eval(&cond) && object.allowed_attribute?(allowed) }
end
你可以这样做:
[:field1, :field2, :field3, :field4].each do |attribute_name|
attribute attribute_name, if: -> { allowed(attribute_name) }
end