TomJ
TomJ

Reputation: 5469

Rails 4 Strong Params has_many with JSON

I'm attempting to pass json up on the client side and have rails take care of handling the object creation.

Here are my models:

class Order < ActiveRecord::Base
  has_many :order_items, :autosave => true
  belongs_to :menu_session
end

class OrderItem < ActiveRecord::Base
  belongs_to :order
  has_one :menu_item
end

Controller

class OrderController < ApplicationController
 #POST /order/create
 def create
   @order = Order.new(order_params)
   @order.save
 end

private
  def order_params
    params.require(:order).permit(:comments, :menu_session_id, :order_items => [:menu_item_id])   
  end
end

The json data:

{'order': {'comments': 'none', 'menu_session_id': '9', 'order_items':[{'menu_item_id': '5'}, {'menu_item_id': '5'}]}};

The javascript

var data = {};
data.order = {'comments': 'none', 'menu_session_id': '9', 'order_items':[{'menu_item_id': '5'}, {'menu_item_id': '5'}]};
$.post('http://localhost:3000/order/create', orders, function(){}, 'json');

Finally, the error log:

Started POST "/order/create" for 127.0.0.1 at 2013-07-10 22:30:36 -0400
Processing by OrderController#create as JSON
Parameters: {"order"=>{"comments"=>"none", "menu_session_id"=>"9", "order_items"=>{"0"=>{"menu_item_id"=>"5"}, "1"=>{"menu_item_id"=>"5"}}}}
Completed 500 Internal Server Error in 52ms

ActiveRecord::AssociationTypeMismatch (OrderItem(#28109220) expected, got Array(#16050620)):
app/controllers/order_controller.rb:5:in `create'

Clearly, either my json is messed up or the ruby .permit is wrong. However, I've been playing with variations of this for a while now and cannot get it to work. The official documentation doesn't seem to venture into this, and every example I have found here deals with forms.

Anyone have any idea what is going on? I can't be the first to try this approach.


UPDATE:

Worked around it by making the following changes:

class OrderController < ApplicationController

  #POST /order/create
  def create
    @order = Order.new(order_params)
    order_items = order_item_params
    order_items.each do |item|
      @order.order_items << OrderItem.new(menu_item_id: item)
    end
    @order.save
  end


  private
    def order_params
      params.require(:order).permit(:comments, :menu_session_id)   
    end
    def order_item_params
      params.require(:order_items)
    end
end

json: {"order":{"comments":"none","menu_session_id":"9"},"order_items":["5","5"]}

I don't think this would be the best way to do it, so I'm going to leave the question unanswered for now in hopes there is a best practice.

Upvotes: 1

Views: 3044

Answers (1)

Alex Braha Stoll
Alex Braha Stoll

Reputation: 146

The workaround is not necessary in this case. ActiveRecord provides an automagic way of creating child elements directly through the params hash. In order to accomplish this, follow the steps bellow:

  1. Configure Nested Attributes in the model

    class Order < ActiveRecord::Base
      # autosave is already enabled with accepts_nested_attributes_for
      has_many :order_items
      belongs_to :menu_session
    
      accepts_nested_attributes_for :order_items
    end
    
  2. Include a *_attributes key in your JSON. In your case, change the order_items key to order_items_attributes

    {'order': {'comments': 'none', 'menu_session_id': '9', 'order_items_attributes':[{'menu_item_id': '5'}, {'menu_item_id': '5'}]}};
    
  3. In your controller, make permit accept your new key

    def order_params
      params.require(:order).permit(:comments, :menu_session_id, :order_items_attributes => [:menu_item_id])   
    end
    

There is some more awesomeness possible to accomplish with Nested Attributes. For further information, see ActiveRecord::NestedAttributes at Rails API

Upvotes: 3

Related Questions