13

我已经搜索了相关问题,但通过从我的 AngularJS 前端返回的 JSON 更新 rails 4 中的嵌套属性仍然存在问题。

问题:下面的代码概述了从 AngularJS 传递到我的 Rails4 应用程序中的候选模型的 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 模型包含以下代码,用于定义与 works 模型的 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

我很感激你能提供的任何帮助,因为我确信我错过了一些相当简单的东西。

谢谢!

4

3 回答 3

7

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

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 中的此问题以查看他们是否/何时解决此问题。

更新 10/17
在此处等待 PR 合并:rails/rails#19254

于 2014-12-22T20:14:13.877 回答
6

我找到了一种基于 Rails 文档解决问题的方法:http ://edgeapi.rubyonrails.org/classes/ActionController/ParamsWrapper.html

基本上,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 数据,请为这个问题添加另一个答案

于 2013-10-26T16:07:53.907 回答
1

您还可以通过将其放入例如wrap_parameters.rb初始化程序中来猴子补丁参数包装以始终包含nested_attributes:

    module ActionController
        module ParamsWrapper

            Options.class_eval do
                def include
                    return super if @include_set

                    m = model
                    synchronize do
                        return super if @include_set
                        @include_set = true
                        unless super || exclude
                            if m.respond_to?(:attribute_names) && m.attribute_names.any?
                                self.include = m.attribute_names + nested_attributes_names_array_of(m)
                            end
                        end
                    end
                end

                private 
                    # added method. by default code was equivalent to this equaling to []
                    def nested_attributes_names_array_of model
                        model.nested_attributes_options.keys.map { |nested_attribute_name| 
                            nested_attribute_name.to_s + '_attributes' 
                        }
                    end
            end

        end
    end
于 2016-02-28T12:59:01.407 回答