在Rails中使用JSON创建嵌套对象

时间:2009-05-10 13:51:58

标签: ruby-on-rails ruby json

如何将JSON传递给RAILS应用程序,以便在has_many关系中创建嵌套的子对象?

这是我到目前为止所拥有的:

两个模型对象。

class Commute < ActiveRecord::Base
  has_many :locations
  accepts_nested_attributes_for :locations, :allow_destroy => true
end

class Location < ActiveRecord::Base
  belongs_to :commute
end

使用Commute,我有一个标准的控制器设置。我希望能够使用JSON在单个REST调用中创建Commute对象以及多个子Location对象。我一直在尝试这样的事情:

curl -H "Content-Type:application/json" -H "Accept:application/json" 
-d "{\"commute\":{\"minutes\":0, 
\"startTime\":\"Wed May 06 22:14:12 EDT 2009\", 
\"locations\":[{\"latitude\":\"40.4220061\",
\"longitude\":\"40.4220061\"}]}}"  http://localhost:3000/commutes

或者更具可读性,JSON是:

{
    "commute": {
        "minutes": 0,
        "startTime": "Wed May 06 22:14:12 EDT 2009",
        "locations": [
            {
                "latitude": "40.4220061",
                "longitude": "40.4220061"
            }
        ]
    }
}

当我执行时,我得到了这个输出:

Processing CommutesController#create (for 127.0.0.1 at 2009-05-10 09:48:04) [POST]
  Parameters: {"commute"=>{"minutes"=>0, "locations"=>[{"latitude"=>"40.4220061", "longitude"=>"40.4220061"}], "startTime"=>"Wed May 06 22:14:12 EDT 2009"}}

ActiveRecord::AssociationTypeMismatch (Location(#19300550) expected, got HashWithIndifferentAccess(#2654720)):
  app/controllers/commutes_controller.rb:46:in `new'
  app/controllers/commutes_controller.rb:46:in `create'

看起来正在读入JSON数组的位置,但不会将其解释为Location对象。

我可以轻松更改客户端或服务器,因此解决方案可以来自任何一方。

那么,RAILS让我这么容易吗?或者我是否需要为我的Commute对象添加一些支持?也许添加一个from_json方法?

感谢您的帮助。


正如我一直在努力解决的那样,一个有效的解决方案是修改我的控制器。但这似乎不是“铁路”的做法,所以如果有更好的方法,请让我知道。

def create
    locations = params[:commute].delete("locations");
    @commute = Commute.new(params[:commute])

    result = @commute.save

    if locations 
      locations.each do |location|
        @commute.locations.create(location)
      end
    end


    respond_to do |format|
      if result
        flash[:notice] = 'Commute was successfully created.'
        format.html { redirect_to(@commute) }
        format.xml  { render :xml => @commute, :status => :created, :location => @commute }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @commute.errors, :status => :unprocessable_entity }
      end
    end
  end

2 个答案:

答案 0 :(得分:36)

想出来,location对象应该被称为locations_attributes以匹配Rails嵌套对象创建命名方案。完成后,它与默认的Rails控制器完美配合。

{
    "commute": {
        "minutes": 0,
        "startTime": "Wed May 06 22:14:12 EDT 2009",
        "locations_attributes": [
            {
                "latitude": "40.4220061",
                "longitude": "40.4220061"
            }
        ]
    }
}

答案 1 :(得分:0)

如果您无法轻松更改正在使用的json,则可以在模型上实施new。我正在使用以下内容将json与预期形状纠缠在一起,在我的情况下,我需要删除“id”键和一些本地模型中不存在的键。我在application_record.rb中实现了这一点,并在每个模型中都有特殊情况。

 def self.new(properties = {})
  super properties.select { |attr, _val| 
    (['id'] + attribute_types.keys).include?(attr.to_s) 
}