has_many

We'll need to implement has_many relationships on both Photo and Slideshow. Figure 3-2 shows the mapping between Active Record objects and database tables with has_many.

The entity (slideshow) has_many associations (slides) relationship is a one-to-many relationship

Figure 3-2. The entity (slideshow) has_many associations (slides) relationship is a one-to-many relationship

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. Table 3-2 shows you the metaprogramming for has_many.

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]