Question

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?

Was it helpful?

Solution

Credits to http://moeffju.net/blog/using-bigint-columns-in-rails-migrations

class CreateDemo < ActiveRecord::Migration
  def self.up
    create_table :demo, :id => false do |t|
      t.integer :id, :limit => 8
    end
  end
end
  • See the option :id => false which disables the automatic creation of the id field
  • The t.integer :id, :limit => 8 line will produce a 64 bit integer field

OTHER TIPS

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

UPDATE: @Notinlist, to use arbitrary column for primary key on arbitrary tables you need to do the create_table-change_column dance:

create_table(:users) do |t|
  # column definitions here..
end
change_column :users, :id, :float # or some other column type

e.g. if I wanted guid instead of auto-increment integers,

create_table(:users, :primary_key => 'guid') do |t|
  # column definitions here..
end
change_column :users, :guid, :string, :limit => 36

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.

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.

If anyone needs this to work with PostgreSQL, create an initializer like this:

# config/initializers/bigint_primary_keys.rb
ActiveRecord::Base.establish_connection
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:primary_key] = 'bigserial primary key'

Because of lazy loading in Rails 3.2 (and maybe even earlier versions), ActiveRecord::ConnectionAdapters::PostgreSQLAdapter won't be required until you establish the database connection.

In rails4, you can do it.

Following is an example to create a Dummy model in rails4 & postgres,

xxx_migrate_dummies.rb:

class CreateDummies < ActiveRecord::Migration
  def change
    create_table :dummies, :id => false do |t|
      t.column :id, :serial8, primary_key: true
      t.string :name, :limit => 50, null: false
      t.integer :size, null: false

      t.column :create_date, :timestamptz, null: false
    end
  end
end

What it did:

  • It use serial8 as id type, which is 64 bit integer, and define it as primary key.
  • It use timestamptz as datetime type, which contain the timezone info, this is important for a application that go across multiple timezones.

Rails 3, MySQL:

t.column :foobar, :int, :limit => 8

Does not give me a bigint, only an int. However,

t.column :twitter_id, 'bigint'

works fine. (Although it does tie me to MySQL.)

Borrowing from other solutions, adjusted for what worked for me recently.

Add to a file in config/initializers. It declares a new column type (adapted from chookeat's suggestion).

ActiveRecord::ConnectionAdapters::Mysql2Adapter::NATIVE_DATABASE_TYPES[:long_primary_key] = "BIGINT(20) DEFAULT NULL auto_increment PRIMARY KEY"

Migrations that use a long id are as such:

    create_table :notification_logs, :id => false do |t|

      t.column :id, :long_primary_key
      # ...
    end

I wrote a gem called activerecord-native_db_types_override that lets you alter the datatypes that will be used in your migrations.

In your Gemfile, add:

gem 'activerecord-native_db_types_override'

then in config/environment.rb, to use long ids in postgres, add:

NativeDbTypesOverride.configure({
  postgres: {
    primary_key: { name: "bigserial primary key"}
  }
})

See its README for up-to-date info.

You can do it like this:

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users, id: :bigserial do |t|
      t.string :name
    end
  end
end

Correction to how to change the default primary key column type:

Instead of:

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

you should do:

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

or else you won't be able to add foreign key restrictions in the database layer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top