Postgres UUID in Rails

2014/03/17

Intro & Setup

Rails 4 has native support for UUID in PostgreSQL so I decided to give it a try in my test project.

First, you need to enable PostgreSQL extension 'uuid-ossp'. Create a new migration like this:

rails generate migration enable_uuid_ossp

And edit the newly created migration file

class EnableUuidOssp < ActiveRecord::Migration
  def change
    enable_extension 'uuid-ossp'
  end
end

After that, run rake db:migrate. After this, you can use start using :uuid as your table's primary key in other migrations. For example:

rails generate migration create_users
class CreateUsers < ActiveRecord::Migration
  def change
   create_table :users, id: :uuid  do |t|
      t.string :username
      t.string :fullname
      t.string :email
      t.string :encrypted_password
      t.timestamps
    end
  end
end

You can also use :uuid not as ID replacement but on a specific column

class AddSuperIdToStudents < ActiveRecord::Migration
  def change
    add_column :students, :super_id, :uuid
  end
end

Drawbacks

Using UUID as ID replacement will make Model.first and Model.last methods not working anymore (UUID is all about randomness after all). Luckily, you can use created_at attribute and implement default_scope in your model as following:

class User < ActiveRecord::Base
  default_scope -> { order('created_at ASC') }
end

Or you can define you own scopes using created_at:

class User < ActiveRecord::Base
  scope :first, -> { order("created_at").first }
  scope :last, -> { order("created_at DESC").first }
end

Another problem is that t.references method in your migrations. If your users table have UUID as ID and you define reference to it in other tables using t.references :user, it will create a user_id column with integer as the type in those tables. Of course, it's not going to work. You must specifically define the reference like this:

...
t.uuid :user_id
...