Как построить ответ JSON, составленный из нескольких моделей в Rails - PullRequest
11 голосов
/ 03 апреля 2010

Сначала желаемый результат

У меня есть User и Item модели. Я хотел бы создать ответ 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"}
  ]
}

Однако у моей модели User и Item больше атрибутов, чем у них. Я нашел способ заставить это работать, но остерегайтесь , это не красиво ... Пожалуйста, помогите ...

Обновление

Следующий раздел содержит оригинальный вопрос. В последнем разделе показано новое решение.


Мои хаки

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

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

Смотри, мама! Нет больше хаков!

Переопределить as_json вместо

Документы ActiveRecord :: Serialization # as_json довольно редки. Вот краткое изложение:

as_json(options = nil) 
  [show source]

Для получения дополнительной информации о to_json против as_json см. Принятый ответ для Переопределение to_json в Rails 2.3.5

Код без хаков

user.rb

class User < ActiveRecord::Base

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

end

item.rb

class Item < ActiveRecord::Base

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

end

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

Ответы [ 4 ]

9 голосов
/ 03 апреля 2010

EDITED для использования as_json вместо to_json. См. Как переопределить to_json в Rails? для подробного объяснения. Я думаю, что это лучший ответ.

Вы можете визуализировать нужный 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

Убедитесь, что для ActiveRecord::Base.include_root_in_json установлено значение false, иначе вы получите атрибут 'user' внутри 'user'. К сожалению, похоже, что массивы не передают options каждому элементу, поэтому collect необходим.

1 голос
/ 21 июня 2018

Если кто-то ищет альтернативное решение для этого, я решил это в Rails 4.2:

def observe
  @item = some_item
  @user = some_user

  respond_to do |format|
    format.js do
      serialized_item = ItemSerializer.new(@item).attributes
      serialized_user = UserSerializer.new(@user).attributes
      render :json => {
        :item => serialized_item, 
        :user => serialized_user
      }
    end
  end
end

Возвращает сериализованную версию обоих объектов в формате JSON, доступную через response.user и response.item.

0 голосов
/ 29 августа 2011

Сейчас существует множество новых Gems для сборки JSON, для этого случая наиболее подходящим является Jsonify:

https://github.com/bsiggelkow/jsonify https://github.com/bsiggelkow/jsonify-rails

Это позволяет вам создавать смесь атрибутов и массивов из ваших моделей.

0 голосов
/ 03 апреля 2010

Рабочий ответ # 2 Чтобы избежать проблемы с "экранированием" вашего json, создайте структуру данных вручную, а затем вызовите to_json для нее один раз . Это может быть немного многословно, но вы можете сделать все это в контроллере или абстрагировать его для отдельных моделей как to_hash или как-то еще.

def observe
  respond_to do |format|
    format.js do
      render :json => {
        :user => {:username => current_user.username, :foo => current_user.foo, :bar => current_user.bar},
        :items => @items.collect{ |i| {:id => i.id, :name => i.name, :zim => i.zim, :gir => i.gir} }
      }
    end
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...