はじめに
Rails 6 に追加された新機能を試す第111段。 今回は、uniqueness validation with belongs_to
編です。
Rails 6 では、 belongs_to
, has_many
の関係があるModel で、 uniqueness
制約がある場合に save!
メソッドで例外が発生しないバグが修正されました。
(なお、 後述しますが、 accepts_nested_attributes_for
を組み合わせた場合のバグは修正されていません。)
Ruby 2.6.5, Rails 6.0.1 で確認しました。 (Rails 6.0.0 で修正されています。)
$ rails --version
Rails 6.0.1
今回は、Author モデルと Book モデルを作って、スクリプトで動作確認してみます。
Rails プロジェクトを作成する
$ rails new rails_sandbox
$ cd rails_sandbox
Author モデルを作る
$ bin/rails g model Author name
Book モデルを作る
Book モデルを作ります。 Book は、 title
と isbn
と Author
モデルへの参照を持つようにします。
$ bin/rails g model Book title isbn author:references
Author モデルを修正する
Author モデルに has_many
を追加します。
class Author < ApplicationRecord
has_many :books, dependent: :destroy
end
Book モデルに validation を追加する
Book モデルに uniqueness
の Validation を追加します。 uniqueness
であることが重要です。
class Book < ApplicationRecord
belongs_to :author
validates :isbn, uniqueness: true
end
スクリプトを作成する
Author と Book のデータをDBに保存するスクリプトを作成します。uniqueness 制約にひっかかるように、Book は2件作成します。
author = Author.new(name: 'Dave Thomas')
author.books.build(title: 'Pragmatic Programmer', isbn: '123')
author.books.build(title: 'Agile Web Development with Rails 6', isbn: '123')
author.save!
p author.persisted?
DB マイグレーションを実行する
$ bin/rails db:create db:migrate
スクリプトを実行する
スクリプトを実行します。 ActiveRecord::RecordInvalid
が発生します。
bin/rails runner scripts/save.rb
...
/usr/local/bundle/gems/activerecord-6.0.1/lib/active_record/autosave_association.rb:419:in
`block in save_collection_association': Validation failed: Books is invalid (ActiveRecord::RecordInvalid)`
Rails 5では
エラーにならず、保存されてしまいます。
補足1
uniqueness
以外の validation では、Rails 5 でも Rails 6 でも例外が発生します。
補足2
Author
モデルに、 accepts_nested_attributes_for :books
を追加すると、例外が発生しなくなってしまいます。
(uniqueness
以外の validation では、例外が発生します。)
この事象については、既存の issue が見つかりました。
class Author < ApplicationRecord
has_many :books, dependent: :destroy
accepts_nested_attributes_for :books # https://github.com/rails/rails/issues/20676
end
試したソース
試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try111_save_with_associated