3.2. has_many
We'll need to implement has_many
relationships on both Photo and Slideshow.
"#rubyrails-chp-3-fig-2">Figure 3-2
shows the mapping between Active Record objects and database tables
with has_many.
has_many is the other side of a
belongs_to relationship, so you don't need to modify the
class or table for Slide. You can merely add the
relationship has_many to slideshow.rb:
class Slideshow < ActiveRecord::Base
has_many :slides
end
And now, photo.rb:
class Photo < ActiveRecord::Base
has_many :slides
validates_presence_of :filename
end
By specifying that a photo has many slides, you
give users the ability to use the same photo in several different
slideshows. Remember: a slide is a photo and a position in a
specific slideshow. So a slide can't be reused, but a photo
can.
That's all you have to do to manage the second
side of the relationship. Now, you can to see all of the slides
associated with a photo, and all of the slides in a slideshow. As
usual, you can open the console to see the model in action:
>> slide = Slide.find 1
...
>> slideshow = slide.slideshow
...
>> slideshow.slides.each {|slide| puts slide.photo.filename}
balboa_park.jpg
camel.jpg
cat_and_candles.jpg
hut.jpg
mosaic.jpg
polar_bear.jpg
police.jpg
sleeping_dog.jpg
stairs.jpg
So you get a list of slides in the slideshow,
and each has an associated photo. Active Record is now managing the
has_many relationship between Slideshow and
Slide. You could use photo.slides in the same
way. "#rubyrails-chp-3-table-2">Table
3-2 shows you the metaprogramming for has_many.
|
Many-to-one relationships introduce some
problems for persistence frameworks. Primarily, the framework
designer has to decide whether deleting a parent object also
deletes child objects as well. Automatic deletion of dependent
objects is called cascading
deletes. Sometimes, you want automatic deletion to happen.
For example, deleting an invoice should also delete the line items
for that invoice. But sometimes, you want related objects to stay:
employees should not be deleted when a department is dissolved. If
you define a relationship with the :dependent option,
deleting a row also deletes the associated objects. For example, to
define an invoice, you might specify your invoice like this:
class Invoice < ActiveRecord::Base
has_many :line_items, dependent => true
end
With this definition, deleting an invoice would
also delete associated line items, each with a separate query.
Sometimes, using a separate query to delete each child is
unnecessarily inefficient, but there's a remedy. If the line items
belong to one invoiceand only oneyou can set the
exclusively_dependent parameter on has_many to
true, and Active Record will delete all dependent objects
with one query.
Similarly, when you read an object, you need to decide whether to
load dependent objects. By default, Active Record does not cascade
loads. But you can load children when you load a parent by using
the :include option on any finder.
|
Table 3-2. Metaprogramming for
has_many
|
Added feature
|
Description
|
|
Methods
|
|
<associations><< object
|
Add an object to the
<associations> collection:
photo.slides << a_slide
|
|
<associations>.delete object
|
Delete an object in the
<associations> collection. The objects will be
destroyed if the dependent parameter of has_many is set to
TRue:
photo.slides.delete a_slide
|
|
<associations>_singular_ids
collection
|
Replace the <associations>
collection with a collection of objects identified by ids
in the collection:
photo.slides_singular_ids [1, 2, 3, 4]
|
|
<associations>.find
|
Uses the same rules as a basic find, but
operates only on the items in the <associations>
collection:
photo.slides.find_by_position 4
|
|
<associations>.clear
|
Delete all of the objects in the
association:
photo.slides.clear
|
|
<associations>.empty?
|
Test to see if <associations>
collection is empty:
photo.slides.clear
|
|
<associations>.size
|
Return the number of items in the
<associations> collection:
photo.slides.size
|
|
<associations>.build
|
Build an object of the associated type, but do
not initialize it to the root object. It takes a hash map of
attributes for the new object as a parameter:
slide.build_photo(:filename =>
"cat.jpg"
In this example, photo.slide is
initialized to nil.
|
|
<associations>.create
|
Create an object of the associated type,
initialized to the root object. It takes a hash map of attributes
for the new object as a parameter:
slide.build_photo(:filename =>
"cat.jpg"
In this example, photo.slide is
initialized to slide.
|
|
Attributes
|
|
<associations>
|
A collection of the associated objects:
slide.photos[4]
|
 |