【问题标题】:Nested object creation with JSON in Rails在 Rails 中使用 JSON 创建嵌套对象
【发布时间】:2010-10-25 03:02:54
【问题描述】:

如何将 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 数组正在读取位置,但未解释为位置对象。

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

那么,RAILS 是否能让我轻松做到这一点?还是我需要在我的 Commute 对象中添加一些对此的支持?也许添加一个 from_json 方法?

感谢您的帮助。


正如我一直在解决这个问题,一种可行的解决方案是修改我的控制器。但这似乎不是“rails”的做法,所以如果有更好的方法,请告诉我。

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

【问题讨论】:

    标签: ruby-on-rails ruby json


    【解决方案1】:

    想通了,locations 对象应该被称为locations_attributes 以匹配Rails 嵌套对象创建命名方案。完成此操作后,它可以与默认的 Rails 控制器完美配合。

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

    【讨论】:

    • 您似乎可以控制源 JSON,但如果不能,您将如何处理“位置”?
    • 看一看,我刚刚发布了一个已删除答案的编辑。
    • 对于仍然遇到此问题的任何人,您必须手动允许控制器中的属性。示例:params.require(:commute).permit( :minutes, :startTime, locations_attributes: [ :latitude, :longitude ])
    • 如何自动生成 locations_attributes 而不是 locations 作为 API?例如:commute.as_json(include: :edificacoes) # =&gt; {"commute": {"locations_attributes": [{"latitude": "40.4220061", "longitude": "40.4220061"}]}
    【解决方案2】:

    如果您无法轻松更改正在使用的 json,您可以在模型上实现 new。我正在使用以下内容将 json 调整为预期的形状,在我的情况下,这涉及删除“id”键和一些在我的本地模型中不存在的键。我在application_record.rb 和每个模型中的特殊情况下实现了这个。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-19
      • 2017-02-14
      • 1970-01-01
      • 2013-03-26
      • 1970-01-01
      相关资源
      最近更新 更多