如何在ActiveStorage中更新附件(Rails 5.2)

zar*_*tra 10 activerecord ruby-on-rails ruby-on-rails-5 rails-activestorage

我最近将我的项目升级到最新的Rails版本(5.2)以获得ActiveStorage- 一个处理附件上传到AWS S3,Google Cloud等云服务的库.

几乎一切都很好.我可以上传和附加图像

user.avatar.attach(params[:file])
Run Code Online (Sandbox Code Playgroud)

接收它

user.avatar.service_url
Run Code Online (Sandbox Code Playgroud)

但现在我想替换/更新用户的头像.我以为我可以跑

user.avatar.attach(params[:file])
Run Code Online (Sandbox Code Playgroud)

再次.但这会引发错误:

ActiveRecord::RecordNotSaved: Failed to remove the existing associated avatar_attachment. The record failed to save after its foreign key was set to nil.
Run Code Online (Sandbox Code Playgroud)

那是什么意思?如何更改用户头像?

Car*_*III 10

导致错误的原因

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.
Run Code Online (Sandbox Code Playgroud)

在尝试创建新配置文件时,ActiveRecord尝试user_id将原始配置文件设置为nil,这会使belongs_to记录的外键约束失败.我相信这实际上是当您尝试使用ActiveStorage将新文件附加到模型时发生的事情......这样做会尝试使原始附件记录的外键无效,这将失败.

解决方案

has_one关系的解决方案是在尝试创建新记录之前销毁相关记录(即在尝试附加另一个记录之前清除附件).

user.avatar.purge # or user.avatar.purge_later
user.avatar.attach(params[:file])
Run Code Online (Sandbox Code Playgroud)

这是理想的行为吗?

在尝试为has_one关系添加新记录时,ActiveStorage是否应该自动清除原始记录是一个不同的问题,最好对核心团队提出......

IMO让它与所有其他has_one关系一致工作是有道理的,并且可能最好让开发人员明确清除原始记录,然后再附加新记录而不是自动执行(这可能有点冒昧) ).

资源:

  • 此答案同一天的提交修复了此问题:https://github.com/rails/rails/commit/656ee8b2dd1b06541f03ea19302d3529085f5139#diff-220c5602a1721b74f7ee61a8e0969da (3认同)

yba*_*art 8

您可以在使用purge_later之前调用:attachhas_one_attached

user.avatar.purge_later
user.avatar.attach(params[:file])
Run Code Online (Sandbox Code Playgroud)

更新

Rails 现在自动清除以前的附件(自 8 月 29 日起)