Rails 4不通过JSON更新嵌套属性

时间:2013-10-24 19:13:56

标签: ruby-on-rails json ruby-on-rails-3 ruby-on-rails-4 nested-attributes

我已经搜索了相关问题,但仍然在使用我的AngularJS前端返回的rails 4到JSON中更新嵌套属性时遇到问题。

问题:下面的代码概述了从我的Rails4应用程序中从AngularJS传递到Candidate模型的JSON。 Candidate模型有很多Works,我试图通过Candidate模型更新Works模型。由于某种原因,Works模型无法更新,我希望有人可以指出我缺少的东西。谢谢你的帮助。


这是候选人AngularJS前端的json:

{"id"=>"13", "nickname"=>"New Candidate", "works_attributes"=>[
{"title"=>"Financial Analyst", "description"=>"I did things"},
{"title"=>"Accountant", "description"=>"I did more things"}]}

Rails然后通过添加候选标头将此JSON转换为以下内容,但不包括候选标头下的嵌套属性,无法通过候选模型更新works_attributes

{"id"=>"13", "nickname"=>"New Candidate", "works_attributes"=>[
{"title"=>"Financial Analyst", "description"=>"I did things"},
{"title"=>"Accountant", "description"=>"I did more things"}],
"candidate"=>{"id"=>"13", "nickname"=>"New Candidate"}}

candidate_controller.rb包含一个简单的更新:

class CandidatesController < ApplicationController

    before_filter :authenticate_user!

  respond_to :json

  def update
    respond_with Candidate.update(params[:id], candidate_params)
  end

private

  def candidate_params
    params.require(:candidate).permit(:nickname,
      works_attributes: [:id, :title, :description])
  end

end

candidate.rb模型包含以下代码,用于定义与作品模型的has_many关系:

class Candidate < ActiveRecord::Base

  ## Model Relationships
  belongs_to :users
  has_many :works, :dependent => :destroy  

  ## Nested model attributes
  accepts_nested_attributes_for :works, allow_destroy: true

  ## Validations
  validates_presence_of :nickname
  validates_uniqueness_of :user_id

end

最后,works.rb模型定义了has_many关系的另一面:

class Work < ActiveRecord::Base
  belongs_to :candidate
end

我感谢您提供的任何帮助,因为我确信我错过了一些相当简单的事情。

谢谢!

3 个答案:

答案 0 :(得分:7)

我也在使用Rails和AngularJS之间的JSON API。我使用与RTPnomad相同的解决方案,但找到了一种不必硬编码include属性的方法:

class CandidatesController < ApplicationController
  respond_to :json

  nested_attributes_names = Candidate.nested_attributes_options.keys.map do |key| 
    key.to_s.concat('_attributes').to_sym
  end

  wrap_parameters include: Candidate.attribute_names + nested_attributes_names,
    format: :json

  # ...
end

请参阅Rails中的issue,了解他们是否/何时解决此问题。

更新10/17
等待公关合并:rails/rails#19254

答案 1 :(得分:6)

我找到了一种基于rails http://edgeapi.rubyonrails.org/classes/ActionController/ParamsWrapper.html

的rails文档来解决我的问题的方法

基本上,Rails ParamsWrapper默认启用从前端包装JSON,并在Rails中使用根元素,因为AngularJS不会在根包装元素中返回数据。以上文档包含以下内容:

  

“在没有:include或:exclude选项集的ActiveRecord模型上,它只会包装类方法attribute_names返回的参数。”

这意味着我必须使用以下语句显式包含嵌套属性,以确保Rails包含所有元素:

class CandidatesController < ApplicationController

    before_filter :authenticate_user!
    respond_to :json
    wrap_parameters include: [:id, :nickname, :works_attributes]
    ...

如果有更好的方法在AngularJS和Rails之间传递JSON数据,请为此问题添加另一个答案

答案 2 :(得分:1)

你也可以将patch参数包装设置为始终包含nested_attributes,方法是将其放入例如function convertNumberToWords(amount) { var words = new Array(); words[0] = ''; words[1] = 'One'; words[2] = 'Two'; words[3] = 'Three'; words[4] = 'Four'; words[5] = 'Five'; words[6] = 'Six'; words[7] = 'Seven'; words[8] = 'Eight'; words[9] = 'Nine'; words[10] = 'Ten'; words[11] = 'Eleven'; words[12] = 'Twelve'; words[13] = 'Thirteen'; words[14] = 'Fourteen'; words[15] = 'Fifteen'; words[16] = 'Sixteen'; words[17] = 'Seventeen'; words[18] = 'Eighteen'; words[19] = 'Nineteen'; words[20] = 'Twenty'; words[30] = 'Thirty'; words[40] = 'Forty'; words[50] = 'Fifty'; words[60] = 'Sixty'; words[70] = 'Seventy'; words[80] = 'Eighty'; words[90] = 'Ninety'; amount = amount.toString(); var atemp = amount.split("."); var number = atemp[0].split(",").join(""); var n_length = number.length; var words_string = ""; if (n_length <= 9) { var n_array = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0); var received_n_array = new Array(); for (var i = 0; i < n_length; i++) { received_n_array[i] = number.substr(i, 1); } for (var i = 9 - n_length, j = 0; i < 9; i++, j++) { n_array[i] = received_n_array[j]; } for (var i = 0, j = 1; i < 9; i++, j++) { if (i == 0 || i == 2 || i == 4 || i == 7) { if (n_array[i] == 1) { n_array[j] = 10 + parseInt(n_array[j]); n_array[i] = 0; } } } value = ""; for (var i = 0; i < 9; i++) { if (i == 0 || i == 2 || i == 4 || i == 7) { value = n_array[i] * 10; } else { value = n_array[i]; } if (value != 0) { words_string += words[value] + " "; } if ((i == 1 && value != 0) || (i == 0 && value != 0 && n_array[i + 1] == 0)) { words_string += "Crores "; } if ((i == 3 && value != 0) || (i == 2 && value != 0 && n_array[i + 1] == 0)) { words_string += "Lakhs "; } if ((i == 5 && value != 0) || (i == 4 && value != 0 && n_array[i + 1] == 0)) { words_string += "Thousand "; } if (i == 6 && value != 0 && (n_array[i + 1] != 0 && n_array[i + 2] != 0)) { words_string += "Hundred and "; } else if (i == 6 && value != 0) { words_string += "Hundred "; } } words_string = words_string.split(" ").join(" "); } return words_string; } 初始值设定项中:

wrap_parameters.rb