Rails 7: insert_allとupsert_allで属性のエイリアスを指定可能になる(翻訳)
SQLクエリでエイリアスを使ってテーブル名やカラム名を読みやすくするのは誰もがやったことがあるでしょう。このリネームは一時的な変更であり、元のデータベースのテーブル名は変更されません。
Railsでinsert_all
や upsert_all
で属性のエイリアスがサポートされ、alias_attribute
でActive Recordモデルに追加したエイリアス属性を使えるようになりました。
たとえば、以下のProductモデルにtitle
属性のエイリアスとしてname
を追加したとしましょう。
class Product < ApplicationRecord
alias_attribute :name, :title
end
改修前
#=> Product.insert_all [{ name: "Detergent", description: "A mixture of surfactants with cleansing properties" }], returning: :name
#=> /Users/murtazabagwala/.rvm/gems/ruby-3.0.2/gems/activerecord-7.0.3/lib/active_record/insert_all.rb:264:in `extract_types_from_columns_on': unknown attribute 'name' for Product. (ActiveModel::UnknownAttributeError)
ご覧のように、未知のname
属性があるというエラーが発生します。
改修後
#=> Product.insert_all [{ name: "Detergent", description: "A mixture of surfactants with cleansing properties" }], returning: :name
Product Insert (2.2ms) INSERT INTO "products" ("title","description","created_at","updated_at") VALUES ('Detergent', 'A mixture of surfactants with cleansing properties', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT DO NOTHING RETURNING "title" AS "name"
#=> #<ActiveRecord::Result:0x00007f921ae855e0 @column_types={}, @columns=["name"], @hash_rows=nil, @rows=[["Detergent"]]>
upsert_all
で属性のエイリアスを使えるようになりました。
#=> Product.upsert_all [{ id: 1, name: "Soap"}]
Product Upsert (4.7ms) INSERT INTO "products" ("id","title","created_at","updated_at") VALUES (1, 'Soap', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT ("id") DO UPDATE SET updated_at=(CASE WHEN ("products"."title" IS NOT DISTINCT FROM excluded."title") THEN "products".updated_at ELSE CURRENT_TIMESTAMP END),"title"=excluded."title" RETURNING "id"
# => #<ActiveRecord::Result:0x00007f92185c5650 @column_types={}, @columns=["id"], @hash_rows=nil, @rows=[[1]]>
ご覧のように、insert_all
とupsert_all
で属性のエイリアスを使ってもエラーが発生しなくなりました。
原注: この改良は、公式バージョンのRailsではまだリリースされていません。
詳しくは#45036を参照してください。
概要
元サイトの許諾を得て翻訳・公開いたします。
参考: 週刊Railsウォッチ20220516
insert_all
やupsert_all
でalias_attributes
もサポート#45036は、現時点ではmainブランチにのみマージされています。