LoginSignup
0
0

Deviseでのユーザー情報更新時に関連データの誤更新を防ぐ方法

Posted at

背景

RailsのDeviseライブラリを使用している際、Companyユーザーの情報を更新する際に関連データが誤って更新される問題が発生しました。特に、パスワードの検証が失敗しても、その他の関連モデルの変更(今回の場合はaccept_nested_attributes_forで関連付けられた CompanyBusinessDayCompanyBusinessHours)が保存されてしまいます。

問題の原因

この問題の根本的な原因は、Deviseの更新処理(update_with_password メソッド)が単一のトランザクション内で実行されていないため、パスワードの検証に失敗しても他のデータベース変更がコミットされてしまうことにあります。

解決策

解決策として、Deviseの update アクションをオーバーライドし、全ての更新処理をカスタムトランザクションブロック内で実行します。これにより、すべての変更が正しいパスワードの検証を通過した後にのみコミットされるようになります。

実装方法

以下は、カスタムのトランザクションを用いた update アクションのサンプルコードです。

# app/controllers/companies/registrations_controller.rb
class Companies::RegistrationsController < Devise::RegistrationsController
  def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)

    ActiveRecord::Base.transaction do
      if update_resource(resource, account_update_params)
        yield resource if block_given?
        if is_flashing_format?
          set_flash_message :notice, :updated
        end
        sign_in resource_name, resource, bypass: true
        respond_with resource, location: after_update_path_for(resource)
      else
        # 更新が失敗した場合、ロールバック
        raise ActiveRecord::Rollback
      end
    end
  end

  private

  def update_resource(resource, params)
    resource.update_with_password(params)
  end
end

説明

  • update_resource: Deviseの標準的なパスワードによる更新メソッドを呼び出しています。
  • トランザクションブロック: ActiveRecord::Base.transaction を使用して、すべてのデータベース操作を一つのトランザクションとして管理します。これにより、何らかのステップで失敗した場合に全てのデータベース変更をロールバックすることができます。

この手法により、パスワードの検証が正しく行われ、関連データの安全な更新が保証されます。また、この方法はDeviseを使用している多くのRailsアプリケーションに適用可能です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0