46

New to rails and I'm following the Depot project found in the Agile web development with rails 3.1. Everything was fine until I got lost when the book used the "build" method.

@cart = current_cart
product = Product.find(params[:product_id])
@line_item = @cart.line_items.build(product: product)

My google searches led me to understand that the .build method is just a cleaner way to create a row in the table (with association between tables). But on the code above, I was expecting the code would look like something like this:

@line_item = @cart.line_items.build(product_id => params[:product_id])

I don't understand why the author had to store the whole row of products( product = Product.find(params[:product_id])) instead of just getting the product_id...

Is there more to it than what I can understand?

Finks
  • 1,661
  • 2
  • 16
  • 25

2 Answers2

92

You misunderstood build. It's just an alias of new, nothing special. https://github.com/rails/rails/blob/959fb8ea651fa6638aaa7caced20d921ca2ea5c1/activerecord/lib/active_record/relation.rb#L84

build won't "create" a record in database, just create a new object in memory so that the view can take this object and display something, especially for a form.

For your second question, yes, your way of composing by id will work as well. But a better approach is not to trust param. Instead, verify it by finding in db at first.

Billy Chan
  • 24,625
  • 4
  • 52
  • 68
  • I liked your answer, although nzifnab has the same answer, your tip made me a better developer :) – Finks Nov 04 '13 at 05:38
  • isn't build required when doing through associations? quite different from new – ahnbizcad Oct 03 '14 at 07:00
  • 2
    For clarification - in current Rails, `ActiveRecord::Relation#build` is aliased to `ActiveRecord::Relation#new`, but `ActiveRecord::Associations::CollectionProxy#new` is aliased to `ActiveRecord::Associations::CollectionProxy#build` (the opposite direction). With an association, `build`/`new` will create the new record with the record id's appropriately associated. So, Relation#build and Relation#new have slightly different implementations from CollectionProxy#build and CollectionProxy#new. However, since they are aliased in both classes, then it does not matter if you use `build` or `new`. – sealocal Mar 25 '15 at 00:37
  • Source reference from comment above: https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/associations/collection_proxy.rb#L261 – sealocal Mar 25 '15 at 00:54
  • Build almost behaves like new, but there are edge cases. Particularly for "has_one" relationships, if there already is a "has_one :thing" related, `obj.build_thing` can delete or nullify the foreign key of the existing related item. See: https://stackoverflow.com/questions/53212840/rails-has-one-build-association-deletes-existing-record-even-if-new-record-is-no . – dgsan Apr 28 '20 at 23:01
1

I'm going to go ahead and say you are entirely correct. Either method works and will do the same thing, but your version using just :product_id is more efficient and requires one less database query. That said, it might make sense if you need that product variable later in the code or that specific line item calls product.{something} later so it doesn't have to fetch it by id at that point.

However, I personally would prefer to just set the :product_id, I see no reason to find the object first.

nzifnab
  • 15,876
  • 3
  • 50
  • 65