Zia*_*hal 5 ruby-on-rails rails-activerecord rails-activestorage
我有一个非常简单的场景,我正在创建一个记录,而不是附加一个文件(之后save!,因为我需要记录上的 id 来生成附件的精细名称),所有这些都包含在一个事务中。
就像是:
def create
ActiveRecord::Base.transaction do
record = A.create!(a_params)
pdf = generate_pdf
record.file.attach(
io: StringIO.new(pdf),
filename: "PO##{record.id}.pdf",
content_type: 'application/pdf'
)
rescue
# here it should rollback transaction on all kind of errors, if it fails upload or whatever, but it does not
raise ActiveRecord::Rollback
end
end
Run Code Online (Sandbox Code Playgroud)
但是 activestorage 仅在提交后才上传文件,因此这种内部救援永远不会起作用,有效的是:
def create
record = nil
ActiveRecord::Base.transaction do
record = A.create!(a_params)
pdf = generate_pdf
record.file.attach(
io: StringIO.new(pdf),
filename: "PO##{record.id}.pdf",
content_type: 'application/pdf'
)
end
rescue
record&.destroy!
end
Run Code Online (Sandbox Code Playgroud)
在这里,我简化了这个场景,实际上我有一个场景,在一个循环中创建了很多记录,我不想保存任何记录,以防出现任何错误。
我发现了一些问题,如:https : //github.com/rails/rails/issues/32449 https://github.com/rails/rails/issues/31985
我该如何以最佳方式解决此问题,我读到活动存储会在后台上传文件,以免使交易持续很长时间。因为否则会产生问题。这是有道理的,我认为我也应该将 pdf_generation 逻辑排除在交易之外。
但是我想知道是否有更好的方法可以仅在正确生成 pdf 时创建记录。而不是手动销毁它们并在发生错误时将任何其他更新恢复到数据库。
小智 6
我找到了一个替代方案,仍然可以优雅地验证上传。
首先,使用该方法上传文件ActiveStorage::Blob.create_after_upload!,传递一些文件参数作为io、filename和content_type:
https://apidock.com/rails/v6.0.0/ActiveStorage/Blob/create_after_upload%21/class
然后,您设置并保存blob上传到附件栏。
在你的情况下,可能是这样的:
blob = ActiveStorage::Blob.create_after_upload!(
io: StringIO.new(pdf),
filename: "PO##{record.id}.pdf",
content_type: 'application/pdf')
record.file = blob
record.save!
Run Code Online (Sandbox Code Playgroud)
这样,如果在文件上传到服务期间发生任何错误,它将在当前块之前和内部引发,正如最初预期的那样。
注意:它在 Rails 版本 6.0.x 上进行了测试,所以我不知道它在其他版本上是否运行良好。
| 归档时间: |
|
| 查看次数: |
666 次 |
| 最近记录: |