1

I have a table called Contracts. Its current default primary key is the :id field that Rails automatically generates, which is an integer. I want to have a field called contractId that is a string type and use it as a primary key instead. What I want to know is:

  1. Is this a best practice? Are there any potential issues with doing this?

  2. How I would go about it

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Trung Tran
  • 13,141
  • 42
  • 113
  • 200
  • This is not a "best practice" as far as Rails is concerned. The name and type and other semantics of id (e.g. auto-incrementing) are assumed in many places throughout the code. Of course as the good answers here indicate, you can get around some of these conventions -- mostly in order to support legacy databases not originally intended for use by Rails. Almost everything is possible; not everything is advisable if you can avoid it. (I say this while in the midst of finding a way to deal with updating an old app that did not follow conventions -- every step is a struggle :-) – Tom Harrison Apr 07 '15 at 12:08

2 Answers2

5

Ruby on Rails (RoR) likes to emphasise the concept of convention over configuration. Therefore, it seeks to minimialise the amount of configuration. So if you want contractId that is a string type then you can add one extra field in your table and use it wherever you want and let the Rails use id as primarykey.

Change PrimaryKey

Generate a new migration file name it "ChangePrimaryKey" (You can give any name).

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);"
  end

  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
  end
end

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
      t.timestamps
      execute "ALTER TABLE table ADD PRIMARY KEY (id);"
    end
  end
end

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
end

I hope you can do rest of the things.

Don't change default id if you want to see Rails real Magics :)

Community
  • 1
  • 1
A H K
  • 1,758
  • 17
  • 29
  • Hi - thanks for your help. I tried the following code: class ChangePk < ActiveRecord::Migration def up remove_column :contracts, :id rename_column :contracts, :contractId, :id execute "ALTER TABLE contracts ADD PRIMARY KEY (id);" end def down execute "ALTER TABLE contracts DROP CONSTRAINT table_pkey;" rename_column :contracts, :id, :contractId add_column :contracts, :id, :primary_key end end I keep getting a syntax error around the "ALTER TABLE contracts ADD PRIMARY KEY(id);" part... @ahmadhasankhan – Trung Tran May 24 '14 at 18:24
  • 1
    1. Remove `:id` column 2. Rename `:contractId` column to `:id` 3. run `execute "ALTER TABLE contracts ADD PRIMARY KEY (id);"` - You can also have a look at: [Using Rails, how can I set my primary key to not be an integer-typed column?](http://stackoverflow.com/questions/1200568/using-rails-how-can-i-set-my-primary-key-to-not-be-an-integer-typed-column) - [Problems setting a custom primary key in a Rails 4 migration](http://stackoverflow.com/questions/19050978/problems-setting-a-custom-primary-key-in-a-rails-4-migration) – A H K May 25 '14 at 03:47
4

As you may know, Rails supports changing the primary id column out of the box:

class Contract < ActiveRecord::Base
  self.primary_key = "contractId"
end

Please note that even if the contractId column has a unique index, an index on a string column will always be a bit slower than an index in an integer column.

Furthermore, this is not the Rails way and might confuse other developers that work with this application. Especially building associations or routes are error-prone when your table has a non-standard primary key. IMHO that is a good reason to avoid using this technic as long as possible.

spickermann
  • 100,941
  • 9
  • 101
  • 131
  • I inherited a table with a non-id primary key and it has been nothing but trouble. I also strongly suggest avoiding this pattern. Only sorrow lies down this path. – Dan Sep 07 '22 at 18:41