62

I have a Comment class with a :foreign_key of post_id in the Post class.

class Comment < ActiveRecord::Base
  belongs_to :post, :class_name => "Post", :foreign_key => "post_id", :counter_cache => true
  belongs_to :author, :class_name => "User", :foreign_key => "author_id"
end

But my CreateComments migration does not define a database-level foreign key:

class CreateComments < ActiveRecord::Migration
  def self.up
    create_table :comments do |t|
      t.column "post_id",       :integer,   :default => 0, :null => false
      t.column "author",        :string,    :default => "",   :limit => 25, :null => false
      t.column "author_email",  :string,    :default => "",   :limit => 50, :null => false
      t.column "content",       :text,      :null => false
      t.column "status",        :string,    :default => "",   :limit => 25, :null => false
      t.timestamps
    end
  end

  def self.down
    drop_table :comments
  end
end

Instead post_id is a simple Integer column.

So, it seems that this foreign key relationship exists only in the mind of Rails, not at the database level.

Is this correct?

Also, is it necessary for the corresponding Post model to also declare its reciprocal foreign key relationship with Comments using the :foreign_key attribute or could that be omitted?

class Post < ActiveRecord::Base
  set_table_name("blog_posts")
  belongs_to :author, :class_name => "User", :foreign_key => 'author_id'
  has_many :comments, :class_name => "Comment",
    :foreign_key => 'post_id', :order => "created_at desc", :dependent => :destroy
  has_many :categorizations
  has_many :categories, :through => :categorizations
  named_scope :recent, :order => "created_at desc", :limit => 5

end
Welbog
  • 59,154
  • 9
  • 110
  • 123
pez_dispenser
  • 4,394
  • 7
  • 37
  • 47
  • 1
    I share your surprise that Rails doesn't use SQL foreign keys. It makes it difficult to use non-rails tools on DBs. – Greg Sep 20 '12 at 19:58
  • 4
    Rails follows convention that "ALL business logic should be defined in the application"... so it uses DB only as a "dumb" storage. No foreign keys, no stored procedures, no constraints (supported in postgres for example). EDIT: Just found this answer which says the same - http://stackoverflow.com/questions/8334602/need-to-create-a-foreign-key-when-creating-a-table-on-rails – Dalibor Filus Mar 31 '13 at 18:02
  • 1
    https://github.com/matthuhiggins/foreigner – wberry Jan 21 '14 at 18:57

1 Answers1

83

The Rails default behaviour is that the column used to hold the foreign key on a model is the name of the association with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly. The associations between your Post and Comment model classes should look like this:

class Post < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :post
end

—Note that you don't need :class_name => "Post" in your Comment model. Rails already has that information. You should only be specifying :class_name and :foreign_key when you need to override the Rails' conventions.

You're correct that Rails maintains the foreign key relationships for you. You can enforce them in the database layer if you want by adding foreign key constraints.

John Topley
  • 113,588
  • 46
  • 195
  • 237
  • Since foreign_key connection is not at the database level and only in rails. If a record is deleted from Post, will its associated row in comment will also be deleted? That is another benefit of maintaining foreign_key. Will I get this benefit? – Shruts_me Aug 18 '15 at 16:54
  • 2
    If you want to delete the associated column as well. You need to add `dependent:destroy` in the end of the line you wrote association. – Lavika May 09 '17 at 18:30