156

So I have a create_table like this for Courses at a School:

create_table :courses do |t|
  t.string :name
  t.references :course
  t.timestamps
end

but I want it to reference two other courses like:

has_many :transferrable_as # A Course
has_many :same_as          # Another Course

Can I say the following?

t.references :transferrable_as, :as=> :course
Arslan Ali
  • 17,418
  • 8
  • 58
  • 76
themirror
  • 9,963
  • 7
  • 46
  • 79

5 Answers5

224

You can do this all in the initial migration/column definition (at least currently in Rails 5):

t.references :transferable_as, index: true, foreign_key: {to_table: :courses}
t.references :same_as, index: true, foreign_key: {to_table: :courses}
ndnenkov
  • 35,425
  • 9
  • 72
  • 104
Ryan
  • 17,511
  • 23
  • 63
  • 88
  • 14
    This works on Rails 5.1 and none of the other suggestions do. It's much cleaner, and feels right. – stephenmurdoch May 06 '17 at 02:03
  • 2
    I use Rails 5.1.4 but it doesn't work. When I specify a `foreign_key` option in the table creation like this ways, it raises an error saying the very table I'm creating doesn't exist... So I suspect it's not really supported by the official API. – Quv Nov 07 '17 at 03:53
  • 4
    I also read that `index` is already added to foreign keys as of Rails https://stackoverflow.com/questions/39769981/why-did-rails-5-changed-index-to-foreign-key – Jonathan Reyes Apr 25 '18 at 05:44
  • 2
    from what I've seen, passing `index:true` is redundant, `t.references :transferable_as, foreign_key: {to_table: :courses}` also creates index. – Serge Vinogradov Jun 06 '21 at 10:43
124

You can do it this way:

create_table :courses do |t|
  t.string :name
  t.references :transferrable_as
  t.references :same_as
  t.timestamps
end

or using t.belongs_to as an alias for t.references

You can't add foreign_key: true to those two references lines. If you want to mark them as foreign keys at the database level you need to have a migration with this:

add_foreign_key :courses, :courses, column: :transferrable_as_id
add_foreign_key :courses, :courses, column: :same_as_id

Update

In Rails 5.1 and above you can add the foreign key in the migration in the create_table block like this:

create_table :courses do |t|
  t.string :name
  t.references :transferrable_as, foreign_key: { to_table: 'courses' }
  t.references :same_as, foreign_key: { to_table: 'courses' }
  t.timestamps
end
Toby 1 Kenobi
  • 4,717
  • 2
  • 29
  • 43
  • 5
    The part about not being able to add `foreign_key: true` to the references lines was what was tripping me up. Adding the `add_foreign_key` and specifying the column name for those did the trick. – Matthew Clark Sep 16 '15 at 16:22
  • Does this work out of the box in Rails? According to http://stackoverflow.com/a/22384289/239657, this requires the `schema_plus` gem. Rails' [add_reference](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_reference) docs don't mention a :references options. – Beni Cherniavsky-Paskin Apr 18 '16 at 07:46
  • It works out of the box on Rails 4.2 in my experience. Interesting observation about that option nor being in the docs. Those docs also say that the :foreign_key option is available but I found it wasn't. Perhaps a different method gets called somehow. – Toby 1 Kenobi Apr 18 '16 at 14:19
  • 1
    I'm not following what the `references:` option is for (as opposed to `t.references` wouldn't that only be relevant on the model level, with the foreign_key considerations being taken care of by `add_foreign_key`? – MCB Nov 18 '16 at 06:05
  • 1
    @MCB `t.references` says "add a field to this table that is the primary key of another table." The `references:` option tells it which table it is a primary key of (needed if it is not clear by the name of the field). The `add_foreign_key` function tells the database to enforced referential integrity here. – Toby 1 Kenobi Nov 18 '16 at 08:40
  • @Toby1Kenobi Ok, I ran them both and didn't notice any difference in my schema though – MCB Nov 18 '16 at 20:56
  • @MCB you ran both what exactly? – Toby 1 Kenobi Nov 18 '16 at 20:58
  • @Toby1Kenobi Oh, a migration each way, with a `references: :user` and I rolled it back and ran it again without that, and the schema looked the same both ways. – MCB Nov 18 '16 at 21:13
  • @MCB what did you name the attribute? – Toby 1 Kenobi Nov 18 '16 at 21:16
  • @Toby1Kenobi the line was `t.belongs_to :vendor` and a vendor is a user – MCB Nov 18 '16 at 21:18
  • @MCB oh, well I suppose ActiveRecord doesn't need to know what table it's referencing to make a foreign key, if all primary keys are the same type (integer), but it makes me feel better if it does have access to that info. What if in the future it supports a database back end where the primary key can be a variety of different types? Then the `references:` option would be needed. – Toby 1 Kenobi Nov 18 '16 at 21:24
  • No idea, never handled that situation before, and honestly only have really dealt with a db through AR except when issues were dire. But as far as I know this is all handled at the model level and migrations just handle setting up the DB. Would postgresql ever need this info or just for writing sql? – MCB Nov 18 '16 at 21:32
  • 2
    @MCB after all this time I realise you were right all along. Your first comment above is exactly right - the `add_foreign_key` lines take care of informing the database what is a foreign key of what. The `references:` parameter is doing nothing. – Toby 1 Kenobi Sep 04 '18 at 17:04
15

As an added answer to this question -- the Model should have the following line to complete the association:

    belongs_to :transferrable_as, class_name: "Course"
    belongs_to :same_as, class_name: "Course"
14

I think this thread has a different more Rails-ish way: Scaffolding ActiveRecord: two columns of the same data type

In the migration:

t.belongs_to :transferrable_as

t.belongs_to :same_as

Community
  • 1
  • 1
themirror
  • 9,963
  • 7
  • 46
  • 79
  • 1
    but how does the db know which foreign key to link the table to? I'm trying this with Postgres database and it's giving me an error `PG::UndefinedTable: ERROR` it's trying to add a foreign key constraint to a table that doesn't exist. – Toby 1 Kenobi Aug 10 '15 at 15:46
  • In case anyone is wondering, `belongs_to` is just [an alias](https://github.com/rails/rails/blob/master/activerecord%2Flib%2Factive_record%2Fconnection_adapters%2Fabstract%2Fschema_definitions.rb#L428) to `references` and so has the exact same functionality. – Jason Swett Dec 30 '18 at 16:02
1

I don't think references accepts the :as option, but you can create your columns manually...

create_table :courses do |t| 
  t.string  :name 
  t.integer :course1_id
  t.integer :course2_id 
  t.timestamps 
end 
Ju Nogueira
  • 8,435
  • 2
  • 29
  • 33