Help us understand the problem. What is going on with this article?

なぜかid列がinteger型になっているRailsのテーブルをserial型に変更する手順

困っていたこと

ローカル環境で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

参考文献

jnchito
SIer、社内SEを経て、ソニックガーデンに合流したプログラマ。 「プロを目指す人のためのRuby入門」の著者。 http://gihyo.jp/book/2017/978-4-7741-9397-7 および「Everyday Rails - RSpecによるRailsテスト入門」の翻訳者。 https://leanpub.com/everydayrailsrspec-jp
https://blog.jnito.com/
sonicgarden
「お客様に無駄遣いをさせない受託開発」と「習慣を変えるソフトウェアのサービス」に取り組んでいるソフトウェア企業
http://www.sonicgarden.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした