前提
usersのテーブル定義は以下の状態でサービス稼働中
class User < ActiveRecord::Base
end
$ User.column_names
# => ["id", "name", "created_at", "updated_at"]
ここにカラムemail
を追加すると起きる問題
1. プリペアドステートメントでエラー
以下の記事より
アプリケーションが動作しているプロセス以外からRDBに定義の変更を加えると、トランザクション内部でプリペアドステートメントが発生する場合にエラーになります。
ActiveRecord::PreparedStatementCacheExpired: ERROR: cached plan must not change result type
>PostgreSQLの場合、テーブルに定義的な変更が加わった場合、プリペアドステートメントの再作成を必須としています。
## 2. modelのschema cacheでエラー
ActiveRecordでは`ApplicationRecord`クラスを継承したmodelはrailsがDBのschema情報から、アクセサメソッドなどを追加してくれるのですが、model毎に初回テーブルアクセス時にschema情報をキャッシュしています。
その為、キャッシュしているschema情報と実際のschemaに差分があるとエラーが発生します。
例えば以下は、稼働中に`email`カラムを増やし、cacheが残ったままアクセスした場合に発生します。
ActiveModel::UnknownAttributeError: unknown attribute 'email' for User.
# 対策
## Rails5以降
`ActiveRecord::Base.ignored_columns`で`email`カラムをActive Recordに対して隠蔽する。
```ruby:user.rb
class User < ApplicationRecord
self.ignored_columns = %w(email)
end
$ User.column_names
# => ["id", "name", "created_at", "updated_at"]
Rails4
ActiveRecord::Base.columns
をオーバーライドしてemail
を読み込ませないようにする。
class User < ActiveRecord::Base
def self.columns
super.reject {|column| column.name == 'email'}
end
end
$ User.column_names
# => ["id", "name", "created_at", "updated_at"]
to_jsonを使う場合
to_json
を使用する時は上記では対策できずエラーになるのでexcept
オプションを使用します。
$ JSON.parse(User.all.to_json).first.keys
# => SystemStackError: stack level too deep
$ JSON.parse(User.all.to_json(except: [:email])).first.keys
# => ["id", "name", "created_at", "updated_at"]
参考
https://eagletmt.hateblo.jp/entry/2017/09/24/004709
http://48n.jp/blog/2017/03/06/ignored-columns-rails5/
https://qiita.com/hirokishirai/items/d2374324c8c7d265363f