J'ai récemment mis à niveau mon projet vers la dernière version Rails (5.2) pour obtenir ActiveStorage
- une bibliothèque qui gère les téléchargements de pièces jointes vers des services cloud comme AWS S3, Google Cloud etc.).
Presque tout fonctionne bien. Je peux télécharger et joindre des images avec
user.avatar.attach(params[:file])
et le recevoir avec
user.avatar.service_url
Mais maintenant, je veux remplacer/mettre à jour l'avatar d'un utilisateur. Je pensais pouvoir courir
user.avatar.attach(params[:file])
encore. Mais cela jette une erreur:
ActiveRecord::RecordNotSaved: Failed to remove the existing associated avatar_attachment. The record failed to save after its foreign key was set to nil.
Qu'est-ce que cela signifie? Comment puis-je changer l'avatar d'un utilisateur?
Cette erreur est signalée par le has_one
association entre votre modèle et l'enregistrement de pièce jointe. Cela se produit car si vous essayez de remplacer la pièce jointe d'origine par une nouvelle, celle-ci sera orpheline et provoquera l'échec de la contrainte de clé étrangère pour belongs_to
les associations. C'est le comportement de tous les ActiveRecord has_one
relations (c'est-à-dire qu'il n'est pas spécifique à 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.
En tentant de créer un nouveau profil, ActiveRecord essaie de définir le user_id
du profil d'origine à nil
, ce qui échoue à la contrainte de clé étrangère pour belongs_to
enregistrements. Je crois que c'est essentiellement ce qui se passe lorsque vous essayez de joindre un nouveau fichier à votre modèle à l'aide d'ActiveStorage ... cela essaie d'annuler la clé étrangère de l'enregistrement de pièce jointe d'origine, ce qui échouera.
La solution pour has_one
relations consiste à détruire l'enregistrement associé avant d'essayer d'en créer un nouveau (c'est-à-dire purger la pièce jointe avant d'essayer d'en attacher une autre).
user.avatar.purge # or user.avatar.purge_later
user.avatar.attach(params[:file])
La question de savoir si ActiveStorage doit automatiquement purger l'enregistrement d'origine lorsque vous essayez d'en attacher un nouveau pour les relations has_one est une question différente à poser à l'équipe principale ...
IMO le faire fonctionner de manière cohérente avec toutes les autres relations has_one est logique, et il peut être préférable de laisser au développeur le soin d'expliciter la purge d'un enregistrement original avant d'en attacher un nouveau plutôt que de le faire automatiquement (ce qui peut être un peu présomptueux). ).
Ressources:
Tu peux appeler purge_later
avant attach
lors de l'utilisation de has_one_attached
:
user.avatar.purge_later
user.avatar.attach(params[:file])
Mise à jour
Rails purge désormais automatiquement la pièce jointe précédente (depuis le 29 août) .
J'ai le même problème avec la sauvegarde d'image. J'espère que cela vous aidera
class User < ApplicationRecord
has_one_attached :avatar
end
regardons le formulaire et le contrôleur
= 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