web-dev-qa-db-ja.com

ActiveStorage(Rails 5.2)で添付ファイルを更新する方法

最近プロジェクトを最新のRailsバージョン(5.2)にアップグレードしてActiveStorageを取得しました-AWS S3、Google Cloudなどのクラウドサービスへの添付ファイルのアップロードを処理するライブラリ。

ほとんどすべてが正常に動作します。画像をアップロードして添付できます

user.avatar.attach(params[:file])

そしてそれを受け取る

user.avatar.service_url

しかし今、私はユーザーのアバターを置き換え/更新したいと思います。走れると思った

user.avatar.attach(params[:file])

再び。しかし、これはエラーをスローします:

ActiveRecord::RecordNotSaved: Failed to remove the existing associated avatar_attachment. The record failed to save after its foreign key was set to nil.

それはどういう意味ですか?ユーザーのアバターを変更するにはどうすればよいですか?

11
zarathustra

エラーの原因

このエラーは、モデルと添付レコードの間のhas_one関連付けによって発生しています。これは、元の添付ファイルを新しいものに置き換えようとすると、元のファイルが孤立し、belongs_to関連付けの外部キー制約が失敗するために発生します。これは、すべてのActiveRecord has_one関係の動作です(つまり、ActiveStorageに固有ではありません)。

類似の例

class User < ActiveRecord::Base
   has_one :profile
end
class Profile < ActiveRecord::Base
   belongs_to :user
end

# create a new user record
user = User.create!

# create a new associated profile record (has_one)
original_profile = user.create_profile!

# attempt to replace the original profile with a new one
user.create_profile! 
 => ActiveRecord::RecordNotSaved: Failed to remove the existing associated profile. The record failed to save after its foreign key was set to nil.

新しいプロファイルを作成する際、ActiveRecordは元のプロファイルのuser_idnilに設定しようとしますが、これはbelongs_toレコードの外部キー制約に失敗します。 ActiveStorageを使用してモデルに新しいファイルをアタッチしようとすると、これが本質的に何が起こっていると思います...これにより、元のアタッチメントレコードの外部キーを無効にしようとしますが、失敗します。

ソリューション

has_one関係の解決策は、新しいレコードを作成する前に関連するレコードを破棄することです(つまり、別のレコードをアタッチする前にアタッチメントをパージします)。

user.avatar.purge # or user.avatar.purge_later
user.avatar.attach(params[:file])

これは望ましい動作ですか?

Has_one関係の新しいレコードをアタッチしようとするときにActiveStorageが元のレコードを自動的にパージするかどうかは、コアチームに提起される別の質問です...

IMOが他のすべてのhas_one関係と一貫して機能することは理にかなっており、新しいレコードを添付する前に、自動的に実行するよりも、元のレコードをパージすることを明示するように開発者に任せることが望ましい場合があります(これは少しおかしいかもしれません) )。

リソース:

9

purge_laterを使用する場合、attachの前にhas_one_attachedを呼び出すことができます。

user.avatar.purge_later
user.avatar.attach(params[:file])

更新

Railsは以前の添付ファイルを自動的にパージするようになりました(8月29日以降)

3
ybart

画像の保存に関しても同じ問題があります。これが役に立てば幸い

class User < ApplicationRecord
  has_one_attached :avatar
end

フォームとコントローラーを見てみましょう

= simple_form_for(@user) do |f|
  = f.error_notification
  .form-inputs
    = f.input :name
    = f.input :email
    = f.input :avatar, as: :file

  .form-actions
    = f.button :submit

controllers/posts_controller.rb

def create
    @user = User.new(post_params)
    @user.avatar.attach(params[:post][:avatar])
    respond_to do |format|
      if @user.save
        format.html { redirect_to @user, notice: 'Post was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end
1
Kiry Meas