困っていたこと
ローカル環境でrails db:migrate
を実行すると、何もマイグレーションを適用していないテーブルに以下のようなdiffが発生しました。(RDBMSはPostgreSQLです)
-create_table "brands", id: :serial, force: :cascade do |t|
+create_table "brands", id: :integer, default: nil, force: :cascade do |t|
原因を探ってみると、本来serial型であるべきid列のデータ型がただのinteger型になっているのが原因でした。(以下はRubyMineが生成したDDL文で確認した結果)
-- serial型なら正しい
create table brands
(
id serial not null
constraint brands_pkey
primary key,
-- integer型なのはおかしい
create table brands
(
id integer not null
constraint brands_pkey
primary key,
rails dbconsole
(またはraild db
)で\d some_table
のようにして確認するのもありです。
serial型になっている場合(sequenceが設定されている)↓
\d brands
Table "public.brands"
Column | Type | Modifiers
------------------+-----------------------------+-----------------------------------------------------
id | integer | not null default nextval('brands_id_seq'::regclass)
integer型になっている場合(sequenceが設定されていない)↓
\d brands
Table "public.brands"
Column | Type | Modifiers
------------------+-----------------------------+-----------------------
id | integer | not null
なぜinteger型になっていたのかは僕もよくわかりません。
が、integer型のままでは困るので以下の手順で修正しました。
修正手順
1. 本当にserial型で正しいか確認する
念のため本番環境のデータ型を確認するなどして、本当にそのテーブルのid列がserial型でいいのかどうか確認してください。
(本番環境もinteger型だったりしたら、そっちが正になってしまいます)
2. DBのバックアップを取る
作業に失敗したときにそなえてDBのバックアップ(ダンプ)を取っておきます。
$ pg_dump your_app_development > your_app.dump
作業に失敗したときは以下のコマンドでリストアします。
$ psql -h localhost -d your_app_development < your_app.dump
3. 「次のid値」を確認する
rails dbconsole
で対象テーブルの「次のid値」を確認します。
SELECT MAX(id)+1 FROM brands;
?column?
----------
7
(1 row)
4. sequenceを設定する(=serial型に変更する)
そのまま続けて対象テーブルのid列にsequenceを設定し、dbconsoleを終了します。
-- 7は上で取得した「次のid値」
CREATE SEQUENCE brands_id_seq MINVALUE 7;
ALTER TABLE brands ALTER id SET DEFAULT nextval('brands_id_seq');
ALTER SEQUENCE brands_id_seq OWNED BY brands.id;
-- dbconsole(psql)を終了
\q
5. 動作確認
rails db:migrate
を実行し、schema.rb
が変更されなければ修正完了です。
もしrails db:migrate
実行後にまだ以下のようなdiffが発生するようであれば、serial型に変更できていません。
-create_table "brands", id: :serial, force: :cascade do |t|
+create_table "brands", id: :integer, default: nil, force: :cascade do |t|
念のため、Rails上から対象のモデルを新しく作成したあとに正しいidが設定されていることを確認します。
# sandboxモードでrails consoleを起動
$ rails c --sandbox
> brand = Brands.new(...)
> brand.save!
> brand.id #=> 7が返ってくればOK