Rails :has_many explanation
Trying to understand the effects of :has_many
as its introduced in the Agile Web Development book 4th Edition
The following relationship is set up for the cart
class Cart < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
end
this compliments the associated LineItem
class
class LineItem < ActiveRecord::Base
belongs_to :product
belongs_to :cart
end
All is fine, I fully understand the relationship modelling, and just trying to accept that it just 'works开发者_StackOverflow中文版'. However, in code the author, instead of using the LineItem.find
method to do a search on the underlying table, uses a line_items
object, e.g.
current_item = line_items.where(:product_id => product_id).first
Can someone please explain this, and ultimately I imagine, what the effect of the :has_many
method call actually is? What is the line_items
object, where does it come from? I guess the same question will apply to the effect of the other relational Rails methods.
Thanks.'
Consider my answer just as a very big comment to Chris Kimpton's answer.
First, you should read the API documentation, where the associations are explained pretty nicely.
In short, when you call the method has_many
in the code of a class (remember that in Ruby every line is an executed code, so the has_many :something
is just a call to some existing method) then that method defines another two methods with the same name as the argument you have passed.
In this case that would be the Symbol :line_items
, so the has_many
method makes something roughly equivalent to : def line_items(force_reload = false)
and def line_items=(objects)
.
The newly created method line_items
returns a collection object of all the LineItem objects filtered by WHERE cart_id = #{self.id}
(this is a simplified example).
That collection object works like an Array, but it also responds to some more methods (like find
or build
) helping you to manage the relation between the Cart object and LineItem.
So, the line:
line_items.where(:product_id => some_id).first
is the equivalent of:
LineItem.where(:cart_id => self.id).where(:product_id => some_id).first
Using the first method (the line_items
collection) you do not need to remember about adding that :cart_id => self.id
to every statement.
If the code is exactly as you have written here, that line_items object must have been set up somehwere else in that code.
The has_many relationship will add a helper method to give the list of associated elements so it could be doing something like this:
cart = Cart.find(1)
line_items = cart.line_items
cart.line_items will return an array of line_items where line_item.cart_id = cart.id
I presume that sample line of code is in a method of the Cart class?
One of the "features" of ActiveRecord / has_many call is to add this pseudo method to your class.
So the Cart class gains a line_items method to access them.
The line_items.where call is searching within the related line_items - not all LineItems, which is what your call would do. It seems to be looking for a line_item related to a specific product - I wonder where the product_id var comes from - method parameter?
On the other side, the belongs_to call on LineItem adds a "cart" method to access the Cart it is in.
Hope this helps, Chris
精彩评论