Changing the Primary Key Type in Ruby on Rails Models

By: on September 30, 2013

Ruby on Rails (RoR) likes to emphasise the concept of convention over configuration. Therefore, it seeks to minimialise the amount of configuration by resorting to some defaults. These defaults are sometimes not desirable, and RoR does not always make it easy to deviate from these defaults.

Primary Keys Type

By default, all the models in RoR has a column called id of the type :primary_key. The various database adapters that RoR uses to talk to the databases will translate the type to the appropriate column type that the database supports. For example, in PostgreSQL, this would be serial primary key.

There are often times when you would like to change the primary key of tables to be of a different type (such as when you are using some generated UDID as the primary key), or you simply want to use multiple columns as the primary key. The latter problem can be solved by using a Ruby Gem but the former is not so easily solved.

Stackoverflow provides a solution to change the type of the primary key, but it is lacking in some ways. Primarily, the schema file generated by Rails will now omit the necessary SQL instructions to actually make the custom primary key column a primary key in the database.

Our Workaround

The first step to changing the primary key involves the migration file.
The span used is similar whether you are changing an existing table or if you are creating a new table.


A migration file might look like this:

class ChangePrimaryKey < ActiveRecord::Migration
  def up
    remove_column :table, :id # remove existing primary key
    rename_column :table, :udid, :id # rename existing UDID column
    execute "ALTER TABLE table ADD PRIMARY KEY (id);"

  def down
    # Remove the UDID primary key. Note this would differ based on your database
    execute "ALTER TABLE table DROP CONSTRAINT table_pkey;"
    rename_column :table, :id, :udid
    add_column :table, :id, :primary_key

If you are creating a new table, your migration might look like this:

class AddTableWithDifferentPrimaryKey < ActiveRecord:Migration
  def change
    create_table :table, id: false do |t|
      t.string :id, null: false
      # other columns
      execute "ALTER TABLE table ADD PRIMARY KEY (id);"

Notice the id: false options you pass into the table — this asks Rails not to create a primary key column on your behalf.

Changes to Model

In the model, it is essential that you add the following line in order for
Rails to programmatically find the column you intend to use as your primary key.

class Table < ActiveRecord::Base
  self.primary_key = :id

  # rest of span

Database Schema

You might now notice that the db/schema.rb file generated by Rails will
look like this:

create_table 'table' id: false, force: true do |t|
  t.string 'id', null: false

  # ... the rest

When you invoke rake db:schema:load to set up a new machine, the table you create will lack a primary column constraint. This is because the custom execute query you added to the migration will never get run, and Rails does not know what goes on in those execute statement.

A hacky solution we came up with was to add a rake task to be run after rake db:schema:load is run.

In your lib/tasks directory, add a file like after_db_schema_load.rake:

# NOTE: Rails does not allow other primary keys to be defined so we have
# to do it here

namespace :your_app do
  namespace :db do
    task :after_schema_load => :environment do
      puts 'Adding primary key for :documents'
      query = 'ALTER TABLE table ADD PRIMARY KEY (id);'

Rake::Task['db:schema:load'].enhance do

Whenever you run rake db:schema:load, the primary key constraint will now be set accordingly.



  1. findchris says:

    Here is a working solution without the hacks:

  2. trnecka says:

    Thank you, I was facing the schema.rb issue once I changed the id type to bigint for one of my tables. This helped me a lot with the rake task workaround.

    Just a note that you may want to enhance the db:test;clone task as well.

  3. Bryan says:

    The schema.rb hack appears to be unnecessary on Rails 4.2.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>