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はテーブルの最大値を設定してくれる。