Ruby on Rails Tuesday, August 29, 2017

This error is being raised by the has_one association between your model and the attachment record. It occurs because trying to replace the original attachment with a new one will orphan the original and cause it to fail the foreign key constraint for belongs_to associations. This is the behavior for all ActiveRecord has_one relationships (i.e. it's not specific to ActiveStorage). 

An analogous example:

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

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

In attempting to create a new profile, ActiveRecord tries to set the user_id of the original profile to nil, which fails the foreign key constraint for belongs_to records. I believe this is essentially what is happening when you try and attach a new file to your model using ActiveStorage… doing so tries to nullify the foreign key of the original attachment record, which will fail. 

The solution for has_one relationships is to destroy the associated record before trying to create a new one (i.e. purging the attachment before trying to attach another one). 

Whether or not ActiveStorage should automatically purge the original record when trying to attach a new one for has_one relationships is a different question best posed to the core team…

IMO having it work consistently with all other has_one relationships makes sense, and it may be preferable to leave it up to the developer to be explicit about purging an original record before attaching a new one rather than doing it automatically (which may be a bit presumptuous).

Resources:
On Friday, August 25, 2017 at 4:51:17 AM UTC-7, Henning wrote:
We currently switched to Rails 5.2 to get the awesome functionalities of ActiveStorage.
Up until now I was able to upload, delete (purge), and show files as expected - but I am not quite sure on how to update an attachment the right way.

Here are the steps to reproduce:

user.avatar.attached?
=> false

user.avatar.attach(io: File.open("~/avatar.png"), content_type: "image/png", filename: "avatar")
=> #<ActiveStorage::Attachment id: 2, name: "avatar", record_type: "User", ...

user.avatar.attached?
=> true

user.avatar.attach(io: File.open("~/avatar2.png"), content_type: "image/png", filename: "avatar2")
=> Exception: ActiveRecord::RecordNotSaved: Failed to remove the existing associated avatar_attachment. The record failed to save after its foreign key was set to nil.

user.avatar.purge
=> nil

user.avatar.attached?
=> false

user.avatar.attach(io: File.open("~/avatar2.png"), content_type: "image/png", filename: "avatar2")
=> #<ActiveStorage::Attachment id: 2, name: "avatar2", record_type: "User", ...


I don't think it should be necessary to purge the first attachment to attach a new one. So how can this be achieved properly?

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-talk+unsubscribe@googlegroups.com.
To post to this group, send email to rubyonrails-talk@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/8e9ab75b-57a8-4452-a61d-532c93571e7f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment