如何在Rails中构建由多个模型组成的JSON响应

mač*_*ček 11 activerecord json ruby-on-rails

首先,期望的结果

我有UserItem模特.我想构建一个如下所示的JSON响应:

{
  "user":
    {"username":"Bob!","foo":"whatever","bar":"hello!"},

  "items": [
    {"id":1, "name":"one", "zim":"planet", "gir":"earth"},
    {"id":2, "name":"two", "zim":"planet", "gir":"mars"}
  ]
}
Run Code Online (Sandbox Code Playgroud)

但是,我UserItem模型有更多的属性而不仅仅是那些.我找到了一种方法让它工作,但要注意,它不漂亮......请帮助......

更新

下一节包含原始问题.最后一节显示了新的解决方案.


我的黑客

home_controller.rb

class HomeController < ApplicationController

  def observe
    respond_to do |format|
      format.js { render :json => Observation.new(current_user, @items).to_json }
    end
  end

end
Run Code Online (Sandbox Code Playgroud)

observation.rb

# NOTE: this is not a subclass of ActiveRecord::Base
# this class just serves as a container to aggregate all "observable" objects
class Observation
  attr_accessor :user, :items

  def initialize(user, items)
    self.user = user
    self.items = items
  end

  # The JSON needs to be decoded before it's sent to the `to_json` method in the home_controller otherwise the JSON will be escaped...
  # What a mess!
  def to_json
    {
      :user => ActiveSupport::JSON.decode(user.to_json(:only => :username, :methods => [:foo, :bar])),
      :items => ActiveSupport::JSON.decode(auctions.to_json(:only => [:id, :name], :methods => [:zim, :gir]))
    }
  end
end
Run Code Online (Sandbox Code Playgroud)

看马!没有更多的黑客!

as_json改为覆盖

ActiveRecord的::系列化#as_json文档是很稀疏.这是简要说明:

as_json(options = nil) 
  [show source]
Run Code Online (Sandbox Code Playgroud)

有关to_jsonvs的更多信息as_json,请参阅Rails 2.3.5中覆盖to_json的已接受答案

代码没有黑客攻击

user.rb

class User < ActiveRecord::Base

  def as_json(options)
    options = { :only => [:username], :methods => [:foo, :bar] }.merge(options)
    super(options)
  end

end
Run Code Online (Sandbox Code Playgroud)

item.rb的

class Item < ActiveRecord::Base

  def as_json(options)
    options = { :only => [:id, name], :methods => [:zim, :gir] }.merge(options)
    super(options)
  end

end
Run Code Online (Sandbox Code Playgroud)

home_controller.rb

class HomeController < ApplicationController

  def observe
    @items = Items.find(...)
    respond_to do |format|
      format.js do
        render :json => {
          :user => current_user || {},
          :items => @items
        }
      end
    end
  end

end
Run Code Online (Sandbox Code Playgroud)

Jon*_*ian 9

编辑使用as_json而不是to_json.请参见如何在Rails中覆盖to_json?详细解释.我认为这是最好的答案.

您可以在控制器中呈现所需的JSON,而无需辅助模型.

def observe
  respond_to do |format|
    format.js do
      render :json => {
        :user => current_user.as_json(:only => [:username], :methods => [:foo, :bar]),
        :items => @items.collect{ |i| i.as_json(:only => [:id, :name], :methods => [:zim, :gir]) }
      }
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

确保ActiveRecord::Base.include_root_in_json设置为false,否则您将在'user'中获得'user'属性.不幸的是,看起来Arrays不会传递options给每个元素,所以这collect是必要的.