LoginSignup
5
6

More than 3 years have passed since last update.

[Rails]外部キーにnilを許可したいけど値を入れる場合は存在チェックしたい場合の実装方法

Last updated at Posted at 2021-04-07

同じタイプのエンティティに親子関係をもたせるときにparent_idのようなカラムを作って親のIDを持たせることがあると思います。

例えば下記のようなテーブルです。
親を持たない場合はNULLを入れる想定のためNULLを許可しています。

Hoges
+----------------+------------------+------+-----+---------+----------------+
| Field          | Type             | Null | Key | Default | Extra          |
+----------------+------------------+------+-----+---------+----------------+
| id             | bigint           | NO   | PRI | NULL    | auto_increment |
| parent_id      | bigint           | YES  |     | NULL    |                |
| created_at     | datetime(6)      | NO   |     | NULL    |                |
| updated_at     | datetime(6)      | NO   |     | NULL    |                |
+----------------+------------------+------+-----+---------+----------------+

Hogeテーブルのモデルを作ると下記のようになります。
nilを許可するためにoptional: trueをつけています。

class Hoge < ApplicationRecord
  belongs_to :parent, class_name: 'Hoge', optional: true
end

ただ、optionalの場合はHogesテーブルに存在しないidをparent_idに入れた場合もバリデーションはOKになってしまいます。
※Hogeテーブルに適切に外部キー制約を付けていれば存在しないIDはを指定した場合はエラー(ActiveRecord::InvalidForeignKey)になります。

そこでタイトルに書いてある通り、nilは許可しつつ存在チェックはしたい場合の書き方を考えてみました。

カスタムバリデーションを使えばなんとでもなるのですが、できる限りシンプルにRailsに組み込まれているバリデーションを使って書いてみました。
もっと良い方法があればぜひ教えて下さい!!
(これを考えながら、そもそもbelongs_toのoptional: trueはnilは許可するけど関連が存在しないIDは許可しないになってくれるとありがたいなーと思ったりしました。)

class Hoge < ApplicationRecord
  belongs_to :parent, class_name: 'Hoge', optional: true
  validates :parent, presence: true, if: :parent_id?
end
5
6
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
5
6