Michael T. Smith
Michael T. Smith

Reputation: 578

Creating a deeply nested object in Rails

I've built a small API that, when posted a JSON object, creates the representative model records. The data looks like this:

{
  "customer": {
    "email": "[email protected]",
    "first_name": "Michael T. Smith",
    "last_name": "",
    "shipping_address_1": "",
    "telephone": "5551211212",
    "source": "Purchase"
  },
  "order": {
    "system_order_id": "1070",
    "shipping_address_1": "",
    "billing_address_1": "123 Your Street",
    "shipping": "0",
    "tax": "0",
    "total": "299",
    "invoice_date": 1321157461,
    "status": "PROCESSING",
    "additional_specs": "This is my info!",
    "line_items": [
      {
        "quantity": "1",
        "price": "239",
        "product": "Thing A",
        "comments": "comments"
        "specification": {
          "width": "12",
          "length": "12",
        },
      },
      {
        "quantity": "1",
        "price": "239",
        "product": "Thing A",
        "comments": "comments"
        "specification": {
          "width": "12",
          "length": "12",
        },
      },
    ]
  }
}

The question is how to create the nested objects. My models are setup as such:

class Order < ActiveRecord::Base
  has_many :line_items
  belongs_to :customer

  accepts_nested_attributes_for :line_items
end

class LineItem < ActiveRecord::Base
  belongs_to :order
  has_many :specifications
end

class Specification < ActiveRecord::Base
  belongs_to :LineItem 
end

I'm trying to create the records using this code:

@order = @customer.orders.build(@data[:order])
@order.save

Is there a better way to do this? Currently I'm getting this error: ActiveRecord::AssociationTypeMismatch in ApiController#purchase_request LineItem(#70310607516240) expected, got Hash(#70310854628220)

Thanks!

Upvotes: 2

Views: 4425

Answers (2)

J&#233;ssica Antunes
J&#233;ssica Antunes

Reputation: 41

You can use accepts_nested_attributes_for like this: (in my example, Products has_many ProductionItineraries and ProductionItineraries belongs_to Products)

model/product.rb

has_many :production_itineraries, dependent: :delete_all
accepts_nested_attributes_for :production_itineraries, allow_destroy: true

model/production_itinerary.rb

belongs_to :product

To instantiate:

products = Product.new
products.production_itineraries.build(other_production_itineraries_fields)

Doing this, after save products, the production_itinerary object will be save automatically, with the respective product_id field

Upvotes: 2

numbers1311407
numbers1311407

Reputation: 34072

accepts_nested_attributes_for defines a new setter method for the association: the original name with _attributes appended to it.

In your case, there is a line_items_attributes= method on your Order model, which is what you need to use to take advantage of the nested attributes feature. Something as simple as swapping the key before building the model would probably work, e.g.:

@data[:order][:line_items_attributes] = @data[:order].delete(:line_items)
@order = @customer.orders.build(@data[:order])
@order.save

Upvotes: 3

Related Questions