Rails 4 Not Updating Nested Attributes Via JSON

♀尐吖头ヾ 提交于 2019-11-30 17:53:09

I've also been working with a JSON API between Rails and AngularJS. I used the same solution as RTPnomad, but found a way to not have to hardcode the include attributes:

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

Refer to this issue in Rails to see if/when they fix this problem.

Update 10/17
Pending a PR merge here: rails/rails#19254.

I figured out one way to resolve my issue based on the rails documentation at: http://edgeapi.rubyonrails.org/classes/ActionController/ParamsWrapper.html

Basically, Rails ParamsWrapper is enabled by default to wrap JSON from the front-end with a root element for consumption in Rails since AngularJS does not return data in a root wrapped element. The above documentation contains the following:

"On ActiveRecord models with no :include or :exclude option set, it will only wrap the parameters returned by the class method attribute_names."

Which means that I must explicitly include nested attributes with the following statement to ensure Rails includes all of the elements:

class CandidatesController < ApplicationController

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

Please add another answer to this question if there is a better way to pass JSON data between AngularJS and Rails

You can also monkey patch parameter wrapping to always include nested_attributes by putting this into eg wrap_parameters.rb initializer:

    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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!