Using Rails, how can I set my primary key to not be an integer-typed column? Using Rails, how can I set my primary key to not be an integer-typed column? database database

Using Rails, how can I set my primary key to not be an integer-typed column?


Unfortunately, I've determined it's not possible to do it without using execute.

Why it doesn't work

By examining the ActiveRecord source, we can find the code for create_table:

In schema_statements.rb:

def create_table(table_name, options={})  ...  table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false  ...end

So we can see that when you try to specify a primary key in the create_table options, it creates a primary key with that specified name (or, if none is specified, id). It does this by calling the same method you can use inside a table definition block: primary_key.

In schema_statements.rb:

def primary_key(name)  column(name, :primary_key)end

This just creates a column with the specified name of type :primary_key. This is set to the following in the standard database adapters:

PostgreSQL: "serial primary key"MySQL: "int(11) DEFAULT NULL auto_increment PRIMARY KEY"SQLite: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"

The workaround

Since we're stuck with these as the primary key types, we have to use execute to create a primary key that is not an integer (PostgreSQL's serial is an integer using a sequence):

create_table :employees, {:id => false} do |t|  t.string :emp_id  t.string :first_name  t.string :last_nameendexecute "ALTER TABLE employees ADD PRIMARY KEY (emp_id);"

And as Sean McCleary mentioned, your ActiveRecord model should set the primary key using set_primary_key:

class Employee < ActiveRecord::Base  set_primary_key :emp_id  ...end


This works:

create_table :employees, :primary_key => :emp_id do |t|  t.string :first_name  t.string :last_nameendchange_column :employees, :emp_id, :string

It may not be pretty, but the end result is exactly what you want.


I have one way of handling this. The executed SQL is ANSI SQL so it will likely work on most ANSI SQL compliant relational databases. I have tested that this works for MySQL.

Migration:

create_table :users, :id => false do |t|    t.string :oid, :limit => 10, :null => false    ...endexecute "ALTER TABLE users ADD PRIMARY KEY (oid);"

In your model do this:

class User < ActiveRecord::Base    set_primary_key :oid    ...end