Search code examples
ruby-on-railsrubyjsonserializationnested-attributes

How to use nested_attributes when processing JSON?


I'm trying to write an update method that processes JSON. The JSON looks like this:

{
  "organization": {
    "id": 1,
    "nodes": [
      {
        "id": 1,
        "title": "Hello",
        "description": "My description."
      },
      {
        "id": 101,
        "title": "fdhgh",
        "description": "My description."
      }
    ]
  }
}

Organization model:

has_many :nodes
accepts_nested_attributes_for :nodes, reject_if: :new_record?

Organization serializer:

attributes :id
has_many :nodes

Node serializer:

attributes :id, :title, :description

Update method in the organizations controller:

def update
  organization = Organization.find(params[:id])
  if organization.update_attributes(nodes_attributes: node_params.except(:id))
    render json: organization, status: :ok
  else
    render json: organization, status: :failed
  end
end

private
  def node_params
    params.require(:organization).permit(nodes: [:id, :title, :description])
  end

I also tried adding accepts_nested_attributes_for to the organization serializer, but that does not seem to be correct as it generated an error (undefined method 'accepts_nested_attributes_for'), so I've only added accepts_nested_attributes_for to the model and not to the serializer.

The code above generates the error below, referring to the update_attributes line in the update method. What am I doing wrong?

no implicit conversion of String into Integer

In debugger node_params returns:

Unpermitted parameters: id
{"nodes"=>[{"id"=>101, "title"=>"gsdgdsfgsdg.", "description"=>"dgdsfgd."}, {"id"=>1, "title"=>"ertret.", "description"=>"etewtete."}]}

Update: Got it to work using the following:

def update
  organization = Organization.find(params[:id])
  if organization.update_attributes(nodes_params)
    render json: organization, status: :ok
  else
    render json: organization, status: :failed
  end
end

private
  def node_params
    params.require(:organization).permit(:id, nodes_attributes: [:id, :title, :description])
  end

To the serializer I added root: :nodes_attributes.

It now all works, but I'm concerned about including the id in node_params. Is that safe? Wouldn't it now be possible to edit the id of the organization and node (which shouldn't be allowed)? Would the following be a proper solution to not allowing it to update the id's:

if organization.update_attributes(nodes_params.except(:id, nodes_attributes: [:id]))

Solution

  • looks super close.

    Your json child object 'nodes' need to be 'nodes_attributes'.

    {
      "organization": {
        "id": 1,
        "nodes_attributes": [
          {
            "id": 1,
            "title": "Hello",
            "description": "My description."
          },
          {
            "id": 101,
            "title": "fdhgh",
            "description": "My description."
          }
        ]
      }
    }