Posted at

Rails6 のちょい足しな新機能を試す62(polymorphic 編)


はじめに

Rails 6 に追加されそうな新機能を試す第62段。 今回は、 polymorphic 編です。

Rails 6 では、 polymorphic な関連のあるモデルで、 dependent: :nullify を指定したとき、モデルを削除したときに xxx_id だけではなく xxx_type も null になるようになりました。

Ruby 2.6.3, Rails 6.0.0.rc1, Rails 5.2.3 で確認しました。Rails 6.0.0.rc1 は gem install rails --prerelease でインストールできます。

$ rails --version

Rails 6.0.0.rc1

今回は、User, Company, Language の 3つのモデルを作ります。

Language はプログラミング言語です。

プログラミング言語の開発者は、User の場合もあれば、 Company の場合もあります。


プロジェクトを作る

rails new rails6_0_0rc1

cd rails6_0_0rc1


モデルを作る

User モデルを作ります。

$ bin/rails g model User name

Company モデルを作ります。

$ bin/rails g model Company name

Language モデルを作ります。 Company や User と紐付けるための developer 属性を追加します。

$ bin/rails g model Language name developer:references


マイグレーションファイルを編集する

Language のマイグレーションファイルを編集します。

developerpolymorphic: trueindex: true をつけます。

developer は null 値でも可とします。 (今回は dependent: nullify をテストするためです。)


db/migrate/20190720051700_create_languages.rb

class CreateLanguages < ActiveRecord::Migration[6.0]

def change
create_table :languages do |t|
t.string :name
t.references :developer, polymorphic: true, index: true # ここを修正する

t.timestamps
end
end
end



モデルを編集する

User モデルに has_many をつけます。 dependent: nullify を指定します。


app/models/user.rb

class User < ApplicationRecord

has_many :languages, as: :developer, dependent: :nullify
end

Company モデルも同様に修正します。


app/models/company.rb

class Company < ApplicationRecord

has_many :languages, as: :developer, dependent: :nullify
end


Language モデルを修正する

Language モデルを修正します。 belongs_topolymorphic オプションを指定します。

developer 属性は、 nil 可とするため、 optional: true を指定します。


app/models/language.rb

class Language < ApplicationRecord

belongs_to :developer, polymorphic: true, optional: true
end


seed データを作る

seed データを作ります。


db/seeds.rb

User.create(

name: 'Matz',
languages: Language.create(
[
{ name: 'Ruby' }
]
)
)

Company.create(
name: 'Microsoft',
languages: Language.create(
[
{ name: 'C#' }
]
)
)



seedデータを登録する

データベースを作って、 seedデータも登録します。

$ bin/rails db:create db:migrate db:seed


rails console で確認する

今回は、 rails console で確認してみましょう。

登録した User を削除します。

bin/rails c

irb(main):001:0> User.first.destroy
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
(0.2ms) BEGIN
Language Update All (0.6ms) UPDATE "languages" SET "developer_id" = $1, "developer_type" = $2 WHERE "languages"."developer_id" = $3 AND "languages"."developer_type" = $4 [["developer_id", nil], ["developer_type", nil], ["developer_id", 1], ["developer_type", "User"]]
User Destroy (0.2ms) DELETE FROM "users" WHERE "users"."id" = $1 [["id", 1]]
(7.0ms) COMMIT
=> #<User id: 1, name: "Matz", created_at: "2019-07-20 06:12:26", updated_at: "2019-07-20 06:12:26">

languages テーブルのレコードも更新していますね。

実際に検索して確認してみます。

irb(main):002:0> Language.find_by(name: 'Ruby')

Language Load (0.6ms) SELECT "languages".* FROM "languages" WHERE "languages"."name" = $1 LIMIT $2 [["name", "Ruby"], ["LIMIT", 1]]
=> #<Language id: 1, name: "Ruby", developer_type: nil, developer_id: nil, created_at: "2019-07-20 06:12:26", updated_at: "2019-07-20 06:12:26">

developer_typedeveloper_idnil になっていることに注意してください。


Rails 5 では

Rails 5 では、users のレコードを削除すると developer_idnil に更新されますが、 developer_typenil に更新されません。

irb(main):001:0> User.first.destroy

User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
(0.1ms) BEGIN
Language Update All (0.5ms) UPDATE "languages" SET "developer_id" = NULL WHERE "languages"."developer_id" = $1 AND "languages"."developer_type" = $2 [["developer_id", 1], ["developer_type", "User"]]
User Destroy (0.2ms) DELETE FROM "users" WHERE "users"."id" = $1 [["id", 1]]
(6.9ms) COMMIT
=> #<User id: 1, name: "Matz", created_at: "2019-07-20 07:08:54", updated_at: "2019-07-20 07:08:54">

 irb(main):002:0> Language.find_by(name: 'Ruby')

Language Load (0.5ms) SELECT "languages".* FROM "languages" WHERE "languages"."name" = $1 LIMIT $2 [["name", "Ruby"], ["LIMIT", 1]]
=> #<Language id: 1, name: "Ruby", developer_type: "User", developer_id: nil, created_at: "2019-07-20 07:08:54", updated_at: "2019-07-20 07:08:54">


試したソース

試したソースは以下にあります。

https://github.com/suketa/rails6_0_0rc1/tree/try062_nullify_polymorphic


参考情報