The past two days, I was having strange problems with database I created from my current problem. Now, the problem was deceivingly simple to solve, and it was basically my oversight of one of the section in the documentation. But still, I found it rather difficult to hunt down the solution on the Internet.
So to increase the chances of someone else finding the same solution, I've decided to post it here.
SORRY: Sorry for poor readability of code. Drupal (or this theme I'm using) doesn't do a good job of formatting code. I'll have better code display once I get my own blog software deployed.
Thing is, when you are creating a has-and-belongs-to-many (HABTM) relationship in Rails (and elsewhere, for that matter), you need a join table. A join table contain references to both associated tables. So, if we have Posts and Categories linked into a HABTM relationship, you have the following code:
# in category.rb (model)
class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
end
#
# in post.rb (model)
#
class Post < ActiveRecord::Base
has_and_belongs_to_many :categories
end
#
# plus *3* migrations:
#
# migration for post
#
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
...
end
end
def self.down
drop_table :posts
end
end
#
# migration for categories
#
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :categories
end
end
#
# migration for join table
#
class CreateJoinTableForCategoriesAndPosts < ActiveRecord::Migration
def self.up
create_table :categories_posts, :id => false do |t|
t.references :post
t.references :category
end
end
def self.down
drop_table :categories_posts
end
end
Now pay close attention to the last migration. First of all, the join table is named so that it contains the names of tables you want to join. That's plural form of your models. Also, the order of the two names must be alphabetical. So, we have Categories then Posts, and not vice versa. Another thing is that the join table does not have a primary key.
When doing a migration, Rails' create_table method automatically creates an auto-incrementing-non-NULL column called ID. Since you don't want this in a join table, you must pass the :id => false argument to create_column call.
NOTE: Don't confuse this with join resources. Those are different, and those do have an ID column.
I have also used the new Rails 2.0 t.references column. This is basically a short version of:
t.integer :post_id
Important thing to note that the column name of t.references is singular (post, not posts), even though the 'references' is in plural.
That's it. You now have a join table that won't be causing any headaches.
Post new comment