Home arrow Ruby-on-Rails arrow Arrays, Associations and the Active Record
RUBY-ON-RAILS

Arrays, Associations and the Active Record


In this third part of a five-part series that delves into Rail's Active Record, you'll learn how to handle many-to-many associations, use an array, and more. This article is excerpted from chapter five of the book Beginning Rails: From Novice to Professional, written by Jeffrey Allan Hardy, Cloves Carneiro Jr. and Hampton Catlin (Apress; ISBN: 1590596862).

Author Info:
By: Apress Publishing
Rating: 5 stars5 stars5 stars5 stars5 stars / 12
February 23, 2010
TABLE OF CONTENTS:
  1. · Arrays, Associations and the Active Record
  2. · Creating Rich Many-to-Many Associations
  3. · Building Conditions for Advanced Finding
  4. · Using an Array

print this article
SEARCH DEVARTICLES

Arrays, Associations and the Active Record
(Page 1 of 4 )

Creating Many-to-Many Associations 

Often, the relationship between two models is many-to-many. This describes a pattern where two tables are connected to multiple rows on both sides. We’ll use this in our events application to add categories to events. If we wanted to allow only one category to be selected for a given event, we could use has_many. But we want to be able to apply multiple categories.

Think about this for a minute: an event can have many categories, and a category can have many events—where would thebelongs_togo in this situation? Neither model belongs to the other in the traditional sense. In Active Record-speak, we refer to this kind of association ashas_and_belongs_to_many(often referred to ashabtmfor short).

Thehas_and_belongs_to_manyassociation works by relying on a join table that keeps a reference to the foreign keys involved in the relationship. The join table sits between the tables we want to join:categoriesandevents. Not surprisingly, then, the join table in this case will be calledcategories_events. Pay particular attention to the table name. It’s formed from the names of each table in alphabetical order, separated by an underscore. In our case, thecin categories comes before thee in events, hence,categories_events. Figure 5-4 illustrates this relationship.


Figure 5-4. The many-to-many relationship between events and categories

Let’s start by adding theCategorymodel. This is a simple matter of generating the model. Since the category schema definition is so simple (consisting of just anamecolumn), we’ll let the generator fill in the migration for us by passing field arguments directly to the generator. Run the following command inside your application root:

./script/generate model Category name:string

You’ll notice that we generated this model a bit differently than the others. We addedname:stringonto the end of thegeneratemethod. This is a shortcut to have your migrations automatically generated with thefield_name:type  that you specify. This is a handy trick when you’re generating simple schemas. Let’s look at that migration and see what was generated, as shown in Listing 5-8.

Listing 5-8. The db/migrate/004_create_categories.rb File

class CreateCategories < ActiveRecord::Migration
  def self.up
    create_table :categories do |t|
      t.column :name, :string
    end
  end

  def self.down
    drop_table :categories
  end
end

We need another migration to create the join table. Let’s do that now by running the following command:

$ ./script/generate migration create_categories_events

Listing 5-9 shows the result.

Listing 5-9. The db/migrate/005_create_categories_events.rb File

class CreateCategoriesEvents < ActiveRecord::Migration
  def self.up

    create_table :categories_events, :id => false do |t|
      t.column :event_id,    :integer
      t.column :category_id, :integer
    end
  end

  def self.down 
    drop_table :categories_events
  end
end

Remember that when usingcreate_table, you don’t need to specify the primary key, as it will be created automatically. Well, in the case of a join table, we actually don’t want a primary key. This is because the join table isn’t a first-class entity in its own right. Since creating tables without primary keys is the exception and not the rule, we need to
explicitly tellcreate_tablethat we don’t want to create anid. Take a close look at the call tocreate_tablein Listing 5-9. We pass in the option:id => false. This preventscreate_tablefrom creating the primary key.

Go ahead and run this migration:

$ rake db:migrate

---------------------------------------
== CreateCategories: migrating ================================================ -- create_table(:categories)
   -> 0.0033s
== CreateCategories: migrated (0.0034s) =======================================

== CreateCategoriesEvents: migrating ==========================================
-- create_table(:categories_events, {:id=>false})
   -> 0.0034s
== CreateCategoriesEvents: migrated (0.0035s) =================================

--------------------------------------------------

With theCategorymodel and the join table in place, we’re ready to let Active Record in on our association. Open theEventandCategory models and add thehas_and_belongs_to_manydeclarations to them, as shown in Listings 5-10 and 5-11.

Listing 5-10. has_and_belongs_to_many Declaration in app/models/event.rb

class Event < ActiveRecord::Base
  belongs_to :user
  has_and_belongs_to_many :categories
end

Listing 5-11. has_and_belongs_to_many Declaration in app/models/category.rb

class Category < ActiveRecord::Base
  has_and_belongs_to_many :events
end

We should also create a few categories to work with. We like to do this with fixtures. Fixtures are textual representations of database records. They can be used to populate your database with data, and they’re a great place to keep so-called seed data. Using fixtures, we’ll kill two birds with one stone: fixtures are easy to load into the database any time we wish, and they’ll be useful later on, when we’re testing our application in Chapter 9. The model generator created an emptycategoriesfixture intest/fixtures/categories.yml. Open it and add a few categories so that it looks like Listing 5-12.

Listing 5-12. The test/fixtures/categories.yml File

conferences:
  id: 1
 
name: Conferences

parties:
 
id: 2
 
name: Parties

concerts:
 
id: 3
 
name: Concerts

readings:
  id: 4
  name: Readings

movies:
  id: 5
  name: Readings

That should do nicely. You can load fixtures using the Rake taskdb:fixtures:load, specifying the fixtures you want to load using theFIXTURESvariable.

$ rake db:fixtures:load FIXTURES=categories

If you need to add more categories later, you can just append them to the fixture file and reload it. We’ll talk more about fixtures in Chapter 9, but this first look should give you an idea of how they work.

Let’s give this a test run now. Get your console ready and run the following commands.

>> event = Event.find(:first)
=> #<Event:0x268157c>
>> category = Category.find_by_name('parties')
=> #<Category:0x26388b8 @attributes={"name"=>"Parties", "id"=>"2"}>
>> event.categories << category
=> [#<Category:0x26388b8 @attributes={"name"=>"Parties", "id"=>"2"}>]
>> event.categories.any?
=> true
>> event.categories.size
=> 1

Here, we automatically associate a category with an event using the<<operator. In this case, when you use<<withhas_and_belongs_to_many, it automatically saves the new association. Some things in Active Record don’t happen until you saysave, but this is one of the examples where that part is done automatically,

We can even do this from the category’s side of the association. Try the following:

>> category.events.empty?
=> false
>> category.events.size
=> 1
>> category.events.find(:first).title
=> "Tiki Party"

We just did the opposite of the previous test. We said thathas_and_belongs_to_manyworks in both directions, right? So, we simply found our new category and asked it for its first event, which we can see is now Tiki Party, because that’s what we associated in the other direction, too.

Usinghas_and_belongs_to_manyis a very simple way to approach many-to-many associations. However, it has its limitations. Before you’re tempted to use it for associating users with events they would like to attend (activities), we should point out that it has no way of storing additional information on the join. What if we wanted to know when someone decided to attend an event, or how many people the attendee is going to bring along? This kind of data fits naturally in the join table. Rails includes another type of association calledhas_many :through, which allows us to create rich joins like this.


blog comments powered by Disqus
RUBY-ON-RAILS ARTICLES

- Ruby-on-Rails Faces Second Security Flaw in ...
- Ruby 2.0 Prepped for February 2013 Release
- Why LinkedIn Switched from Ruby on Rails
- Adding Style with Action Pack
- Handling HTML in Templates with Action Pack
- Filters, Controllers and Helpers in Action P...
- Action Pack and Controller Filters
- Action Pack Categories and Events
- Logging Out, Events and Templates with Actio...
- Action Pack Sessions and Architecture
- More on Action Pack Partial Templates
- Action Pack Partial Templates
- Displaying Error Messages with the Action Pa...
- Action Pack Request Parameters
- Creating an Action Pack Registration Form

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2018 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials