Created
December 28, 2008 19:49
-
-
Save bryanl/41030 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# in terminal | |
In this lesson, we will have a look at two more activerecord | |
relationships: has and belongs to many and has many through. Both of | |
these relationships use a join table, and can be quite tricky to | |
implement at first. We'll start with has and belongs to many and then | |
cover has many through. | |
In our journal we want to be able to classify our entries by how we | |
felt when we wrote them. Maybe we were feeling happy when we wrote | |
the entry, or maybe we were feeling sad. We will want to be able to | |
attach multiple feelings to each entry. One way that we can implement | |
this is by creating a Feeling class and associate it with our Entry | |
class using a join table. Why don't we implement this now? | |
The first thing we'll want to do is generate a Feeling class. Our | |
feelings will only have a name and it'll be a string. We'll use | |
rail's generator for this: | |
$ script/generate model feeling name:string | |
Now before we run this migration, we also want to create a join table | |
as well. We'll create a separate migration for the join table: | |
$ script/generate migration add_join_for_feelings_and_entries | |
We'll now configure our migration to create the join table. | |
# switch to textmate | |
> and_entries.rb | |
def self.up | |
create_table :entries_feelings do |t| | |
t.integer :entry_id | |
t.integer :feeling_id | |
end | |
end | |
def self.down | |
drop_table :entries_feelings | |
end | |
Nowe we we can run our migration | |
# switch to terminate | |
$ rake db:migrate db:test:prepare | |
# switch to textmate | |
We'll now inform entries and feelings that they can have and belong to | |
many of each other. To do this, we'll start with entries. In | |
entries, we will add the relation at the top. | |
> models/entry.rb | |
has_and_belongs_to_many :feelings | |
We'll also need to do the same thing to our feeling class. | |
> models/feeling.rb | |
has_and_belongs_to_many :entries | |
We can test this out in our console in sandbox mode. | |
# switch to the terminal | |
$ ruby script/console -s | |
The first thing we'll do is create a few feelings. | |
>> feelings = {} | |
>> %w[happy sad indifferent full hungry hot cold].each {|f| feelings[f] = Feeling.create :name => f} | |
Now that we have some feelings, we can associate them with entries. | |
>> first_entry = Entry.find(:first) | |
At first our entry has no feelings. We were happy and full when we | |
wrote it, so let's update our entry's feelings. | |
>> first_entry.feelings = [feelings["happy"], feelings["full"]] | |
When we wrote our second entry, we were cold, so we'll update that one as well. | |
>> second_entry = Entry.find(:all)[1] | |
>> second_entry.feelings = [feelings["cold"], feelings["happy"]] | |
Now we can take a look at our feelings to see what entries are | |
related. First, we'll look at our happy feeling. | |
>> feeling["happy"].entries.inspect | |
And we can also look at our cold feeling. | |
>> feeling["cold"].entries.inspect | |
As your application progresses, you might find that you want to add | |
additional attributes to your join table. You might want to add | |
timestamps, or you may want to add additional logic. Rails has an | |
answer this and it is called has many through. What we want to do now | |
is update our relationship between feelings and entries to be a has | |
many through. To do this, we'll need to introduce a new model, and | |
we'll call this model atmosphere. To start off, we'll need to | |
generate our new model. In this instance, we'll create our own migration. | |
# exit the console | |
$ script/generate model --skip-migration atmosphere | |
Now we'll need generate a new migration to add timestamps to our | |
existing table, then we will need to update our relations to use our | |
new model. | |
$ script/generate migration move_entries_feelings_to_atmospheres | |
# switch to textmate | |
> in the migration | |
up: | |
rename_table :entries_feelings, :atmospheres | |
add_column :atmospheres, :updated_at, :datetime | |
add_column :atmospheres, :created_at, :datetime | |
down: | |
remove_columns :atmospheres, :updated_at, :created_at | |
rename_table :atmospheres, :entries_feelings | |
Since we are converting this join to a class, we'll have to make our | |
atmosphere belong to entry and feeling. | |
> models/atmosphere.rb | |
belongs_to :entry | |
belongs_to :feeling | |
Now we'll have to make feelings and entries have many of each other through atmosphere. | |
> models/entry.rb | |
has_many :atmospheres | |
has_many :feelings, :through => :atmospheres | |
> models/feeling.rb | |
has_many :atmospheres | |
has_many :entries, :through => :atmospheres | |
Now we can try this out in our console sandbox. | |
# switch to the terminal | |
$ ruby script/console -s | |
We'll have to create our feelings again | |
>> feelings = {} | |
>> %w[happy sad indifferent full hungry hot cold].each {|f| feelings[f] = Feeling.create :name => f} | |
Let's add a feeling to our first entry. | |
>> entry = Entry.find(:first) | |
>> entry.feelings = feelings["happy"] | |
No we'll have an atmosphere. We can load it. | |
>> atmosphere = Atmosphere.find(:first) | |
Our atmosphere is now associated to our entry and feeling hot. | |
In this lesson we created has and belong to many and has many through | |
relationships. In our next lesson, we'll focus on a feature new to | |
rails 2, named scopes. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment