大規模な移行のスクリプトを作成していて、大きな問題に遭遇しました。
# Import users
user_data.each do |data|
u = User.new
u.id = data.id
u.email = data.email
# more user attributes set...
u.save!
end
# required to prevent Postgres from trying to use now taken user ids
ActiveRecord::Base.connection.execute "ALTER SEQUENCE users_id_seq RESTART WITH #{User.last.id+1};"
そのため、最初にデータ ソースからユーザー データを読み取り、その ID を手動で設定します。関連データも移行するため、ID を保持する必要があります。
その後、関連付けられたオブジェクトのデータから条件付きでさらにユーザーを作成する必要があります。
# Create a user for this email if no user with this email exists.
if data.email
user = User.find_by_email(data.email)
if user
o.user = user
else
o.user = User.create!(
first_name: 'Unknown',
last_name: 'Unknown',
email: data.email,
password: generate_temp_password
)
end
end
これは次の場合に失敗しますUser.create!
:
Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
これを少しデバッグしたところ、このエラーがスローされる直前であることUser.where(email: data.email).first
がわかりました。nil
これは、現在の自動インクリメント値を超えて id を設定することに何か関係があると思われます。何らかの理由で新しいレコードがクエリで見えなくなりますが、Postgres 自身の検証では見えるようになります。
では、特定の電子メールを持つユーザーが存在しないのに、DB 検証エラーをトリガーするにはどうすればよいでしょうか?