LoginSignup
5
1

More than 3 years have passed since last update.

RailsでActive Recordを介さずに、DBにデータを登録したら、そのままではcreateが通らなくなるから注意が必要

Posted at

RailsでDBに直接データを保存すると、何回createアクションをしても、idが重複するようになった?

Active Record便利ですよね。テーブルの結合なども簡単にできるし、一度リレーション作っておけば、多少テーブルの仕様が変わっても、変更のための工数はめっちゃ少なくすみます。

レコードの新規作成等もラクラクで、

例えば、Groupモデル(groupsテーブル)があったとして、データを新規作成しようとした場合、SQLであれば、

INSERT INTO groups(id,name) VALUES(1,'所属A')と書くところを、
Group.create(name:'所属A')と書くことができます。

でも、開発用にいくつかデータを登録したいという時に、


Group.create(name:'所属A')
Group.create(name:'所属B')
Group.create(name:'所属C')

とか書くのはめんど臭いし、かといってseedでデータを生成するのもなぁ・・・

データに投入できるようなエクセルファイルも持っているし、

CSVとかで簡単にDBにデータを投入できたら良いのに・・・

とか思うことがあると思います。

そんな時に、例えばpostgresqlならpgadminや、mysqlならsequel proなどのツールを利用すると、簡単にCSVからデータをDBに流し込むことが出来ます。

(例)csvのデータ

id name
1 飯塚部
2 豊本部
3 角田部
4 東京本部
5 03部隊
6 人力の車輪部

じゃあ、これでDBにCSVでデータを流し込んでと・・・
これで、開発の時用のデータが簡単に登録できだぞ!!

よし、createのデータ新規作成の動きでも確かめるか!とか、思ったら、

Group.create(name:'劇団ふたり部')

RollBack!
PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "groups_pkey"
DETAIL:  Key (id)=(1) already exists.
#↑ん?

なんで、新規作成が通らないの!?

絶対カラム名とか間違えてないのに。

っていうか、なぜ7行目のはずなのに、idが7で登録しようとするの!?だから、idが重複してるって怒られるんじゃん!!

とかなって困った時には、
「Railsのidの自動採番機能(シーケンス、シーケンシャル)」という物に目を向けてみてください。

idの自動採番とは(シーケンス,、シーケンシャル)機能とは

知っての通り、RailsはActive Recordを介して、簡単にDBの操作を行うことができます。

そのActive Recordは、上の例でいうGroup.create(name:'劇団ふたり')のように、プログラマーがidを意識しなくても、idを採番し、レコードの新規作成が容易にできるようにしてくれているようです。
(これがSQL文だと、きちんとidまで指定しないと、insert文を実行することができませんね)

この自動採番機能ですが、Rails側はどうやって、
次に採番すべきidの番号を把握することが出来るのか!?という所に目を向けてみると、
シーケンシャル(シーケンス)と呼ばれる、「今どの番号まで払い出しているか。」という番号を参照しているようです。

このidのシーケンスですが、ActiveRecord(モデル)を介して、レコードの作成が行われた時には、プログラマーが意識することなく、自動で番号を上げてくれるのですが、
今回のように、Railsの機能を通さずに、DBへデータを直接登録した時には、自動で番号を上げてくれません。(そりゃそうですが)

つまり、↑の例でいうと、プログラマー側としては、もうidは6まで採番されているので、次は7に進んで欲しいところが、Rails的にはまだidのシーケンスが1であるため、「idが重複するぞ!!」と怒って新規データが保存されないという状況。

なので、以下のように今のidのシーケンス値はこれですよ〜という風に手動でセットしてあげる必要があります。

psql --dbに接続

select setval('groups_id_seq',6)
setval 
--------
      6
--setval('テーブル名_id_seq',現在振られているidの最大値)という風に設定

ということをすれば、Rails側も次のidは7番だな!
ということが分かり、idの重複もなく、レコードの新規作成が出来るようになります。

ちなみに、このsetvalはpostgresqlの書き方であり、次のようにシーケンスに関する操作を行うことができます。

--現在のシーケンシャルを確認 
select currval('groups_id_seq');
--次のシーケンシャルを確認
select nextval('groups_id_seq');

まとめ

  • Railsの機能を通さずに、DBにレコードを登録した時は要注意
    • idの今の最大値を登録してあげて、Railsが次に採番するidの番号を教えてあげる必要がある
  • 可能ならば、データはseedファイル等で用意してあげるとよいと思う
    • チーム開発の場合、メンバー全員のデータが必要になるし、idのシーケンシャルも気にしなくていいし
5
1
1

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
5
1