Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
UUID primary keys in Rails 3
# Gemfile
gem 'uuidtools'
# db/migrate/20110422210841_create_sites.rb
# 1. :id => false
# 2. :uuid
#
class CreateSites < ActiveRecord::Migration
def self.up
create_table(:sites, :id => false) do |t|
t.string :uuid, :limit => 36, :primary => true
t.timestamps
end
end
def self.down
drop_table :sites
end
end
# app/models/site.rb
class Site < ActiveRecord::Base
include Extensions::UUID
end
# app/models/extensions/uuid.rb
#
module Extensions
module UUID
extend ActiveSupport::Concern
included do
# old rails versions
set_primary_key 'uuid'
# later rails versions, untested:
# self.primary_key = 'the_name'
before_create :generate_uuid
def generate_uuid
self.id = UUIDTools::UUID.random_create.to_s
end
end
end
end
@grosser

you do not need Concern to do this, you can also use the more portable def included(base); base.class_eval do

@apacala

You should add :null => false to the column definition.

@rmoriz
Owner

@grosser
As the description explicitly says, this is for Rails 3. So why not use the framework features. If you want to do this in another Framework, feel free to fork.

@adrpac
To quote Wikipedia: "in the SQL Standard, primary keys may consist of one or multiple columns. Each column participating in the primary key is implicitly defined as NOT NULL" http://en.wikipedia.org/wiki/Unique_key

To quote MySQL manual: "A PRIMARY KEY is a unique index where all key columns must be defined as NOT NULL. If they are not explicitly declared as NOT NULL, MySQL declares them so implicitly (and silently). A table can have only one PRIMARY KEY. "

@rizenine

Why is this 'self.id' and not 'self.uuid'? "self.id = UUIDTools::UUID.random_create.to_s"

@Askegg

@rizenine

Because the primary key has been switched to uuid by set_primary_key 'uuid'. This renders the id column in the database redundant.

@enthooz

Rad gist.

This may be more efficient: t.binary :uuid, :limit => 16, :primary => true

Read this in the comments here: "UUID's are not strings, you should not store them as such in the DB. Your performance is going to be terrible with large datasets. In mysql, the proper way to store a UUID is as a 16-byte binary. If you need to convert it to 7 bit (for use in XML or REST for example) that should be done when the request is served."

@zimbatm

What is the kind of problem you're trying to solve with UUIDs ?

To avoid incremental ids, you could keep using an integer field but generate the ids with rand(2**sizeof(int_column)). Then for display, use obj.id.to_s(16) to get a hex display.

@rizenine

I was exploring the usage of UUID's with MOM as distributed objects.

@fionaom

What happens when you generate your schema for your test Database? rake db:test:prepare

This will generate an ID column with INT(11) & auto increment to true.

Also, all ActiveRecord::Base methods like .find(1234), will be broken, since you no longer have an ID column?

I'm trying to solve this myself, but have reverted back to using INT IDs until I can better understand this.

@Askegg

My understanding is that the "set_primary_key 'uuid'" overrides the default behaviour of rails to use the uuid column instead of the id column. Nothing should break in the process.

@fionaom

That did not work for me.

My test DB was still loaded with ID column with INT(11).

I'm on Rails 2.3.4.

@sethcall

In Postgresql, you can have the database default null columns to a valid UUID, meaning you could do without any customization of rails beyond just writing a raw migration:

CREATE EXTENSION "uuid-ossp";

CREATE TABLE users (
    id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4()
);
@rmoriz
Owner

If you're on postgres you want to look at https://github.com/dockyard/postgres_ext anyways :-)

@alex-ross

Got this when i did run some spec:
DEPRECATION WARNING: Calling set_primary_key is deprecated. Please use self.primary_key = 'the_name' instead.

Haven't looked it up but i guess it's true. So please update folks ;)

@warmwaffles

This isn't the best idea to let rails handle the UUID's. Race conditions are possible and collisions are possible. It's probably best to let MySQL or Postgres handle it via the built in function UUID(). I'm going to do a write up about it soon and how to accomplish UUID's with rails.

@gorn

I was using this approach for a long time, but now after upgrading it stops working. I also get

Got this when i did run some spec:
DEPRECATION WARNING: Calling set_primary_key is deprecated. Please use self.primary_key = 'the_name' instead.

and morover i get new error

uninitialized constant RSpec::Matchers::Extensions::UUID

in rspec tests when trying to use routing paths, which used to work.

Is there a update to this approach? (hopefully not radically different]

@rmoriz
Owner

@warmwaffles you should probably read more about UUIDs and primary key uniqueness ;-)

When possible and you don't mind the database lock-in, do it in the database.

Sometimes, when you have an async API that pushes customer data e.g. on a MQ/redis. You need to generate and return the UUID on the customer-facing system. You can't wait/block for the full, async database round-trip.

@natebird

If you are using Ruby 1.9.3+ you can just call SecureRandom.uuid. You also don't need gem 'uuidtools'.

Thanks for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.