📘
ActiveRecordを単独で使うとbelongs_toが参照先の存在を検証しない
tl;dr
ActiveRecordを単体で(Railsなしで)使うときは、belongs_to
はデフォルトでoptional: true
相当の挙動をします。
Rails同様にoptional: false
をデフォルトにするにはActiveRecord::Base.belongs_to_required_by_default = true
の設定が必要です。
Railsにおけるbelongs_to
RailsでActiveRecordを使う際、belongs_to
は参照先のレコードが存在することを検証します。[Railsガイド]
例えば次のようなテーブルが存在するとします。
ActiveRecord::Schema.define do
create_table :shops do |t|
end
create_table :items do |t|
t.references :shop, null: false, foreign_key: true
end
end
class Shop < ActiveRecord::Base
end
class Item < ActiveRecord::Base
belongs_to :shop
end
このとき、Item#shop
がnil
であるならばItem
はinvalidになります。
p Item.new.valid? #=> false
この検証をやめるには、belongs_to
にoptional: true
というオプションを設定します。
ActiveRecord単体におけるbelongs_to
ところが、ActiveRecordをRailsなしで使うと、この検証は行われません。
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "activerecord"
gem "sqlite3"
end
require "active_record"
require "sqlite3"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Schema.define do
create_table :shops do |t|
end
create_table :items do |t|
t.references :shop, null: false, foreign_key: true
end
end
class Shop < ActiveRecord::Base
end
class Item < ActiveRecord::Base
belongs_to :shop
end
p Item.new.valid? #=> true
上のコードのように、Item#shop
がnil
であってもItem
はvalidになります。
原因と対策
これは、belongs_to
でoptional: false
がデフォルトとなっているのは、ActiveRecordのデフォルトではなくRailsが設定している動作のためです。
従って、ActiveRecordを単体で使う際にoptional: false
をデフォルトとするには、ActiveRecord::Base.belongs_to_required_by_default = true
を書いておく必要があります。
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "activerecord"
gem "sqlite3"
end
require "active_record"
require "sqlite3"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Schema.define do
create_table :shops do |t|
end
create_table :items do |t|
t.references :shop, null: false, foreign_key: true
end
end
###################
# ↓これを追加
###################
ActiveRecord::Base.belongs_to_required_by_default = true
class Shop < ActiveRecord::Base
end
class Item < ActiveRecord::Base
belongs_to :shop
end
p Item.new.valid? #=> false
Discussion