如何将 ActiveModelSerializer 与 group_by 一起使用?

Joh*_*ton 5 activerecord ruby-on-rails

active_model_serializer我在 Rails 中使用。我有两个序列化器:TodoSerializerProjectSerializer

class TodoSerializer < ActiveModel::Serializer
  attributes :id, :project, :note
  has_one :project
end
Run Code Online (Sandbox Code Playgroud)

class ProjectSerializer < ActiveModel::Serializer
  attributes :id, :name
  has_many :todos
end
Run Code Online (Sandbox Code Playgroud)

当我打电话时

render json: @todos
Run Code Online (Sandbox Code Playgroud)

一切都很好,我得到了我期望的输出。但是当我打电话时:

render json: @todos.group_by(&:project_id)
Run Code Online (Sandbox Code Playgroud)

它恢复到另一个 JSON 调用,但不起作用。

我也尝试过:

render json: @todos.group_by(&:project_id), serializer: TodoSerializer
Run Code Online (Sandbox Code Playgroud)

如果我这样做,我会收到错误

> undefined method `id' for #<Hash:0x007fbec2241660>
Run Code Online (Sandbox Code Playgroud)

sma*_*ton 5

我刚刚遇到了类似的问题,并找到了一个我认为可以接受的解决方案:

class GroupedTodoSerializer < ActiveModel::Serializer

  def attributes
    { project_id => serialized_todos }
  end

  private 

  def serialized_todos
    todos.map{ |todo| serialize_todo(todo) }
  end

  def serialize_todo todo
    TodoSerializer.new(todo, root:false) # you can opt out on the root option
  end

  def todos
    object.last
  end

  def project_id
    object.first
  end
end
Run Code Online (Sandbox Code Playgroud)

然后在控制器中你做

render json: @todos.group_by(&:project_id).to_a, each_serializer: GroupedTodoSerializer
Run Code Online (Sandbox Code Playgroud)

这个技巧是有效的,因为您将从 group_by 返回的哈希更改为数组,然后为该数组编写自定义的each_serializer。

我在这里发布了该解决方案的更通用版本:https://makandracards.com/bitcrowd/38771-group_by-with-activemodel-serializers

更新:

我刚刚发现它产生了一个稍微偏离 JSON 字符串,因为它的数组括号距 to_a 太多。

我更新了 makandracard,但这个解决方案可能会在下一个版本的 gem 中崩溃。但0.10.0仍处于RC状态,没有向后兼容性。

class GroupedSomeModelSerializer < ActiveModel::Serializer

  # method override
  def serializable_hash
    @object.map do |some_group_key, some_models|
      [ some_group_key , serialized_some_models(some_models) ]
    end.to_h
  end

  private

    def serialized_some_models some_models
      some_models.map{ |some_model| SomeModelSerializer.new(some_model, root: false) }
    end

end
Run Code Online (Sandbox Code Playgroud)

然后在控制器中:

@some_models = SomeModel.all.group_by(&:some_group_key)
render json: @some_models, serializer: GroupedSomeModelSerializer
Run Code Online (Sandbox Code Playgroud)