[rails6.0.0]ウィザード形式でActiveStorageを使用して画像を保存する方法


概要

ユーザー登録とプロフィールをさせる時に、ウィザード形式でフォームを作成。
プロフィールに画像を保存したかったが上手くいかなかったため備忘録として記載。
※検索しても同じ状況の方がいなかったので役に立てばと思います。

ウィザード形式とは何か?という方はこちらを参考にしてください

内容

開発環境

MacOS Catalina 10.15.6
Rails 6.0.0
Ruby 2.6.5

テーブル構成

deviseを使ってユーザー登録をさせようとしています。
userテーブルはnicknameのみ追加。
profileテーブルにはtwitterのリンクなど記載。
profileテーブルにactive_strageで画像を保存したい。

ウィザード形式を実装

ここらへんの機能の実装に関しては
こちらの記事がかなり近いので参考にしました。

問題点

ウィザード形式で、画像以外はしっかり保存できたけど、画像は保存されない。

registrations_controller
class Users::RegistrationsController < Devise::RegistrationsController

  def create
    @user = User.new(sign_up_params)
    unless @user.valid?
      render :new and return
    end
    session["devise.regist_data"] = {user: @user.attributes}
    session["devise.regist_data"][:user]["password"] = params[:user][:password]
    @profile = @user.build_profile
    render :new_profile
  end

  def create_profile
    @user = User.new(session["devise.regist_data"]["user"])
    @profile = Profile.new(profile_params)
    unless @profile.valid?
      render :new_profile
    end
    @user.build_profile(@profile.attributes)
    @user.save
    session["devise.regist_data"]["user"].clear
    sign_in(:user, @user)
    redirect_to root_path
  end

  private

  def profile_params
    params.require(:profile).permit(:avatar, :favorite_beer, :twitter_link, :info)
  end

この中の以下の記述が悪さをしていた様子。

registrations_controller
@user.build_profile(@profile.attributes)
@user.save

ここでは、@user@profileをbuildで関連付けさせて@user.saveでまとめて保存をしているのですが、ここでの保存が原因で上手く行ってない様子。
この記事を見つけて

原因
ActiveStorageに実際にファイルが保存されるタイミングはmodelをsaveして処理がコミットされた時なので、保存される前のmodelから画像をattachしてもファイルが未完成の状態になるっぽいです。

どうもActiveStrageはモデルをちゃんとsaveしないと行けなさそうなので以下のように記述を変更したら解決しました。

registrations_controller
# それぞれのモデルで保存させた    
# @user.build_profile(@profile.attributes)
@user.save
@profile.user_id = @user.id
@profile.save

まとめ

正直buildを使っての保存の仕組みがよくわかってなかったのに使用してしまったのがエラーの原因かもしれないです。
少しでも参考になれば幸いです。