What determines if rails includes id: :serial in a table definition?
The answer is simply rails 5.0 vs 5.1 migrations. I had previously thought that the project started in 5.1, so I didin't test this. But then I dug deeper and discovered it started in 5.0, and experimentation shows that's the answer.
5.0, no id specified
class SerialIdTest < ActiveRecord::Migration[5.0]
def change
create_table "test" do |t|
t.integer "foo_id"
t.string "foo_role"
end
end
end
create_table "test", id: :serial, force: :cascade do |t|
t.integer "foo_id"
t.string "foo_role"
end
# \d test
Table "public.test"
Column | Type | Modifiers
------------------+-------------------+-------------------------------------------------------
id | integer | not null default nextval('test_id_seq'::regclass)
foo_id | integer |
foo_role | character varying |
Indexes:
"test_pkey" PRIMARY KEY, btree (id)
5.1, no id specified
class SerialIdTest < ActiveRecord::Migration[5.1]
def change
create_table "test" do |t|
t.integer "foo_id"
t.string "foo_role"
end
end
end
create_table "test", force: :cascade do |t|
t.integer "foo_id"
t.string "foo_role"
end
# \d test
Table "public.test"
Column | Type | Modifiers
------------------+-------------------+-------------------------------------------------------
id | bigint | not null default nextval('test_id_seq'::regclass)
foo_id | integer |
foo_role | character varying |
Indexes:
"test_pkey" PRIMARY KEY, btree (id)
5.1, id serial specified
class SerialIdTest < ActiveRecord::Migration[5.1]
def change
create_table "test", id: :serial do |t|
t.integer "foo_id"
t.string "foo_role"
end
end
end
create_table "test", id: :serial, force: :cascade do |t|
t.integer "foo_id"
t.string "foo_role"
end
# \d test
Table "public.test"
Column | Type | Modifiers
------------------+-------------------+-------------------------------------------------------
id | integer | not null default nextval('test_id_seq'::regclass)
foo_id | integer |
foo_role | character varying |
Indexes:
"test_pkey" PRIMARY KEY, btree (id)
When you run rails db:migrate:reset
as opposed to rails db:reset
, the database schema is not loaded from schema.rb
but is instead reconstructed from all of your migrations. In migrations and schema files you do not need to specify an id
field, one is provided by default. However, starting with Rails 5.1, the default size of the id field was increased from INT
to BIGINT
for MySQL and from SERIAL
to BIGSERIAL
for PostgreSQL. So probably there is some interaction between your migrations, schema.rb
, and the actual schema in the database that is causing the id field to be treated as default (and omitted) in some cases and be explicitly specified in others, just due to the change in the default size. It's hard to guess at the source of the problem without seeing all the relevant files.