Matthew Lindfield Seager

Matthew Lindfield Seager

Changing the starting ID in a Rails app

While procrastinating working on a new Rails app, I didn’t want object IDs to start at 1 but neither did I want to deal with the various hassles of using a UUID instead of an integer for the ID. My compromise was to start the ID numbering with a multiple digit number.

This requires adding an execute statement (which is technically non-reversible) to your create_table migration:

class CreateUsers < ActiveRecord::Migration[7.1]
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.string :email, null: false

      t.timestamps
    end

    reversible do |direction|
      direction.up do
        # Set the starting ID on the users table so IDs don't start at 1
        execute <<-SQL
          INSERT INTO sqlite_sequence ('name', 'seq') VALUES('users', 2345);
        SQL
      end

      direction.down do
        # Do nothing (entry is automatically removed when table is dropped)
      end
    end
  end
end

Note: The relevant sequence row gets dropped when the table gets dropped, so in this case there’s no need to define a down action. I’ve included an empty one anyway to make it clear this isn’t an oversight.

Now when I create my first user, their ID is 1 higher than the sequence I assigned… app.example.com/users/2346.

You can choose whatever starting number you want. If you’re running this on a different table, replace the ‘users’ portion with your own table name:

INSERT INTO sqlite_sequence ('name', 'seq') VALUES('<table>', 1734);

The above example is specific to SQLite (I’m using Litestack to simplify deployment) but presumably you could do something very similar with other databases.

I haven’t tested this but for PostgreSQL it should be as simple as changing the execute command above to:

SELECT setval('users_id_seq', 2345);
--            '<table>_id_seq' for other table names

If you try this with PostgreSQL, make you sure test the migration works in both directions.