13

I'm trying to change a Devise User model's id type to a uuid.

My migration looks like this:

class ChangeUserIdTypeToUuid < ActiveRecord::Migration[5.2]

  def up
    change_column :users, :id, :uuid
  end

  def down
    change_column :users, :id, :integer
  end
end

But when I run the migration I get an error:

== 20180909205634 ChangeUserIdTypeToUuid: migrating ===========================
-- change_column(:users, :id, :uuid)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::DatatypeMismatch: ERROR:  column "id" cannot be cast automatically to type uuid
HINT:  You might need to specify "USING id::uuid".
: ALTER TABLE "users" ALTER COLUMN "id" TYPE uuid

There's a hint in there but I don't know what it's suggesting I do. It's not this:

change_column :users, :id, id::uuid

Why is the migration failing? What is the hint suggesting? How do I change the ID type to UUID?

user3574603
  • 3,364
  • 3
  • 24
  • 59
  • 2
    Well maybe I'm mistaken, but you cannot just convert IDs to UUIDs like that. UUIDs are meant to be "very unique" identifiers while ActiveRecord's ID are just natural numbers, so first you'd have to define a strategy to convert a regular ID to an UUID (ie convert 1 to something like `123e4567-e89b-12d3-a456-426655440000`). Then, you'd have to also remember to propagate the change of this ID to all the other table that reference your user ID (if you have any relations on the user model) – Cyril Duchon-Doris Sep 09 '18 at 21:32
  • 1
    Possible duplicate of [Rails 4. Migrate table id to UUID](https://stackoverflow.com/questions/25681599/rails-4-migrate-table-id-to-uuid) – Cyril Duchon-Doris Sep 09 '18 at 21:35

2 Answers2

22

Referring to the post this was suggested to be a duplicate of, I have managed to change the id type to uuid like so:

class ChangeUserIdTypeToUuid < ActiveRecord::Migration[5.2]

  def change
    add_column :users, :uuid, :uuid, default: "gen_random_uuid()", null: false

    change_table :users do |t|
      t.remove :id
      t.rename :uuid, :id
    end
    execute "ALTER TABLE users ADD PRIMARY KEY (id);"
  end
end

The difference from the best answer in the linked question is the way the UUID is generated. I'm using postgresql as my development db so I'm using gen_random_uuid() which is provided by the pgcrypto extension which I had previously enabled in preparation of using UUIDs in another model:

class EnablePgcryptoExtension < ActiveRecord::Migration[5.2]
  def change
    enable_extension 'pgcrypto'
  end
end
user3574603
  • 3,364
  • 3
  • 24
  • 59
  • 7
    I'm not sure the reverse process migration (converting uuids to integers) will work so you may want to make the migration irreversible. You can do this by raising `ActiveRecord::IrreversibleMigration` in the down block. – max Sep 09 '18 at 22:29
1

In my case, I wanted to change the type of existing uuid fields from string to uuid, after having enabled the pgcrypto extension already. I had to specify a using: parameter like so:

def up
  change_column :my_table, :uuid_field_name, :uuid, using: "uuid::uuid"
end

def down
  change_column :my_table, :uuid_field_name, :string
end
pierrea
  • 1,368
  • 14
  • 18