views:

1569

answers:

3

How can I change the (default) type for ActiveRecord's IDs? int is not long enough, I would prefer long. I was surprised that there is no :long for the migrations - does one just use some decimal?

+2  A: 

According to the Rails API documentation, the possible options for type are:

:string
:text
:integer
:float
:decimal
:datetime
:timestamp
:time
:date
:binary
:boolean

You can use :decimal, or you can execute a command directly if you need to:

class MyMigration
  def self.up
    execute "ALTER TABLE my_table ADD id LONG"
  end
end

As wappos pointed out, you can use auxiliary options like :limit to tell ActiveRecord how large you want the column to be. So you would use the :int column with a larger :limit.

Yehuda Katz
"As wappos pointed out, you can use auxiliary options like :limit to tell ActiveRecord how large you want the column to be. So you would use the :int column with a larger :limit."I don't think that will work if what he wants is larger than integer will hold. Setting a larger limit won't change the maximum size.
Luke Francl
in fact :primary_key does not have the :limit option, so this does not work for the primary key column.
Björn
I just looked it up in the Rails docs and if use :limit => 8 on an integer column you will get a bigint. I did not realize that.
Luke Francl
+1  A: 

This is hard to set for the primary key with migrations because Rails puts it in automatically.

You can change any column later like this:

change_column :foobars, :something_id, 'bigint'

You can specify non-primary IDs as custom types in your initial migration like this:

create_table :tweets do |t|
  t.column :twitter_id, 'bigint'
  t.column :twitter_in_reply_to_status_id, 'bigint'
end

Where I have "bigint" you can put any text that your database would use for the database column type you want to use (e.g., "unsigned long").

If you need your id column to be a bigint, the easiest way to do it would be to create the table, then change the column in the same migration with change_column.

With PostgreSQL and SQLite, schema changes are atomic so this won't leave your database in a weird state if the migration fails. With MySQL you need to be more careful.

Luke Francl
Thanks - it seems for a "normal" column using limit => 8 would do the trick, but for the primary_key this does not work. For MySQL I have now used custom SQL with execute. Would prefer change_column, but wouldn't that have the same problem as initial creation, namely that :limit => 8 would not be supported for primary key columns?
Björn
No, you can use change_column with the primary key, like this: change_column :foobars, :id, "bigint". So you'd create the table and then immediately change the ID column to be a bigint. I still don't think using :limit with an int column is going to work (with MySQL anyway) because int's max size is 2**31-1 no matter what.
Luke Francl
OK, I just looked it up in the Rails docs and if you do t.column :foobar, :int, :limit => 8 you will get a bigint.
Luke Francl
+9  A: 

To set the default primary key column type, the migration files are not the place to mess with.

Instead, just stick this at the bottom of your config/environment.rb

ActiveRecord::ConnectionAdapters::MysqlAdapter::NATIVE_DATABASE_TYPES[:primary_key] = "BIGINT UNSIGNED DEFAULT NULL auto_increment PRIMARY KEY"

And all your tables should be created with the intended column type for id:

+--------------+---------------------+------+-----+---------+----------------+
| Field        | Type                | Null | Key | Default | Extra          |
+--------------+---------------------+------+-----+---------+----------------+
| id           | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |

After you've done what you've set out to do... the next question is probably "How do I make my foreign key columns the same column type?" since it does not make sense to have primary key people.id as bigint(20) unsigned, and person_id be int(11) or anything else?

For those columns, you can refer to the other suggestions, e.g.

t.column :author_id, 'BIGINT UNSIGNED'
t.integer :author_id, :limit => 8