views:

4707

answers:

4

I have the following Rails migration which works perfectly (irrelevant pieces removed):

create_table :comments do |t|
  t.text :body
  t.references :post
end

Now I'd like to add an author column to my comments table (which is the userid of a user), but I have no idea how to do it (I'm tempted to just write the MySql-specific syntax using an execute).

I've been looking at add_column here which doesn't mention references. I've actually found TableDefinition#references but I have no idea how to use it with an add_column statement.

Is this possible? Also, is it true that, for MySql, the "references" functionality does not actually establish relationships between the tables?

A: 

You could add the column by add_column(:table, :column_name, :type, :options) in a new Migration.

Thanks Mike. What would the :type and :options be?
Yar
+8  A: 

First, do:

script/generate migration AddAuthorIdToComments

Open the generated file and add this line:

add_column, :comments, :author_id, :integer

Then in your model files:

class User < ActiveRecord::Base
  has_many :comments, :foreign_key => "author_id"
end

class Comment
  belongs_to :author, :class_name => User
end
Milan Novota
very nice, thanks for that. Of course adding the column as an integer is not what I was hoping for, but since that's what the original migration does anyway... THANKS for outlining how to setup the relationships in the model classes.
Yar
Simple truth is, you just can't use references in this scenario, because is only available for table definitions. And yes, as Craig says, migrations don't care about setting foreign keys in database.
Milan Novota
Okay, I get it re: table definitions: at the column level it's not available (only at the table level). A question: if the column in User is called "id", it works, right? "author_id" is the name of the new column in comments ONLY, right?
Yar
Yes, the connection between those two is defined on the model level (see foreign_key, class_name).
Milan Novota
Great, thanks again for the answer and the help.
Yar
What does the "class_name => User" do?
AnApprentice
+1  A: 

It's been a while since I've looked at this, but last I checked migrations don't support creating foreign keys. Fortunately, however, there is a plug-in for it. I've used this and it works well.

Craig Stuntz
Thanks for that, Craig.
Yar
+3  A: 

While it's too late to get any points out of this, I thought I'd post the best way for posterity :)

use change_table instead of create_table to add columns to a table that already exists, with all the TableDefinition goodness:

self.up do
  change_table :comments do |t|
    t.references :author
  end
end

This might seem trivial, but other gems like Devise make heavy use of their own custom table definitions, and this way you can still use them.

Jaime Bellmyer
Okay, so I add an author field to the comments table and I use references. Does that make a difference in MySql? Do I still need to change the model?
Yar
Yes, you'd still need to add "has_many :comments" in your author.rb, and "belongs_to :author" in your comment.rb. The migration code only creates fields in the database, which are useless until you call the right ActiveRecord methods I've listed here.
Jaime Bellmyer
Is this still the best way in rails3? Also, this should really be marked the answer...
The Doctor What
Yeah, this is the best way. In fact, I just used it in a rails 3 app this weekend (during Rails Rumble). And I'm almost 2 years late to be marked the right answer - this is an old question!
Jaime Bellmyer
@Jaime Bellmyer if you use @yar in your comment and you say why this answer beats the best answer, I'll mark it.
Yar
@yar - this is the best answer because the user was asking how to get table definitions like `t.references`, which you can't do with add_column. The Devise gem implements its own custom table definitions, so you don't have to fill your migrations with dozens of tough-to-remember field names. Hopefully, this practice will become more popular with gems and plugins in rails.
Jaime Bellmyer
@Jaime Bellmyer, okay +1 and best answer and I've also learned something. I'll have to check out the Devise gem, too, I guess. Thanks!
Yar