3
4

More than 5 years have passed since last update.

RailsのseedでIDを直接指定する場合

Posted at

Railsで初期データ投入をする際は、整合性の問題からIDは直接指定したい。
しかし、seedでIDを指定してしまうと、seedが終わって、いざデータを作成という段階で怒られてしまう。

irb
irb(main):023:0> boat = IrishCoffee::Boat.new(name: "Tester2")
.
.
irb(main):024:0> boat.save
.
.
ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "ic_boats_pkey"
DETAIL:  Key (id)=(2) already exists.
: INSERT INTO "ic_boats" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"
    from /usr/local/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `async_exec'

明らかに重複したIDを割り振ろうとしていることが解る。
では、IDはどうやって管理されているのかが気になるが、SQLのスキーマを見れば一目瞭然。

kinkai_develop=# \d
                  List of relations
 Schema |         Name         |   Type   |  Owner
--------+----------------------+----------+----------
 public | ar_internal_metadata | table    | kinkaicc
 public | ic_boats             | table    | kinkaicc
 public | ic_boats_id_seq      | sequence | kinkaicc
 public | ic_clubs             | table    | kinkaicc
 public | ic_clubs_id_seq      | sequence | kinkaicc
 public | ic_races             | table    | kinkaicc
 public | ic_races_id_seq      | sequence | kinkaicc
 .
 .

 kinkai_develop=# select * from ic_boats_id_seq;
  sequence_name  | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called
-----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
 ic_boats_id_seq |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
(1 row)

このtablename_id_seqのlast_value又はmax_valueがIDを管理しているのだろことは簡単に想像が付く。
しかし、このシーケンスをどうやって変更すれば良いかが思いつかなかったが、
http://edywrite.blogspot.jp/2012/07/ruby-on-rails3_31.html
にそのままが書かれていたので、ありがたく拝借することにする。

SQLでシーケンスを設定すればいいらしい。

 def reset_id(tablename)
  connection = ActiveRecord::Base.connection()
  connection.execute("select setval('#{tablename}_id_seq',(select max(id) from #{tablename}))")
end

上記をseedsに書いておいて、データの投入後に呼び出してやればIDはテーブルの最大値を設定してくれる。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4