【发布时间】:2017-12-04 21:17:15
【问题描述】:
我正在使用 Rails 5 构建一个 Web 应用程序,当我在关联记录上定义了非持久属性(使用 attr_accessor)时,我遇到了通过父记录更新关联记录的问题。具体来说,我让用户以某种方式在子记录上提供 non-persistent 属性,并根据这些属性的值,将值分配给 persistent 中的属性before_save 回调。问题是子记录不会保存到数据库中(因此不会调用保存回调),除非通过父项更改子记录的持久属性。
我在几种不同的情况下都遇到过这个问题,但这里的(简化的)示例涉及使用Paperclip Gem 处理客户端浏览器上传到AWS S3 的图像。
app/models/dog.rb
class Dog < ApplicationRecord
has_many :certificates, :dependent => :destroy
accepts_nested_attributes_for :certificates, :allow_destroy => true
end
app/models/certificate.rb
class Certificate < ApplicationRecord
# load with path to client-uploaded file on S3 and save to
# update digitized_proof attachment
attr_accessor :s3_key
belongs_to :dog
has_attached_file :digitized_proof,
:content_type => { :content_type => ['image/jpg', 'image/png'] }
before_save :fetch_digitized_proof_from_s3
def fetch_digitized_proof_from_s3
return unless self.s3_key.present?
# note `S3_BUCKET` is set in the aws initializer
s3_obj = S3_BUCKET.object(self.s3_key)
# load paperclip attachment via S3 presigned URL
s3_presigned_url = s3_obj.presigned_url(:get,
:expires_in => 10.minutes.to_i)
self.digitized_proof = URI.parse(s3_presigned_url)
end
end
apps/controllers/dogs_controller.rb 摘录
def update
@dog = Dog.find(params[:id])
if @dog.update(dog_params)
redirect_to ...
...
end
private
def dog_params
params.require(:dog).permit(
...,
:certificates_attributes => [:id, :_destroy, :s3_key]
)
end
我编写了 javascript,可以直接从客户端的浏览器将图像上传到 S3 存储桶中的临时文件夹,并将 s3_key 添加到更新表单中,以便可以在服务器端识别和处理图像(请参阅 @987654329证书.rb 中的@ 方法)。问题是证书永远不会更新,除非更新参数中的实际数据库属性已更改。
为什么会发生这种情况,我该如何解决?
示例参数
{
...,
certificates_attributes: [
{id: '1', _destroy: '0', s3_key: 'tmp/uploads/certificates/.../photo.jpg'},
{id: '2', _destroy: '0', s3_key: 'tmp/uploads/certificates/.../photo2.jpg'}
]
}
宝石版本
rails-5.0.0
activerecord-5.0.0
paperclip-5.1.0
aws-sdk-2.10.0
编辑
我可以通过在 s3_key 的 setter 方法中调用 fetch_digitized_proof_from_s3 来完成证书更新(并删除 before_save 回调):
# app/models/certificate.rb
def s3_key=(key)
@s3_key = key
self.fetch_digitized_proof_from_s3
end
这会触发相关证书正确保存(我认为这是因为digitized_proof,这是一个持久属性,通过调用fetch_digitized_proof_from_s3 更新)。这可行,但我仍然宁愿在保存记录时从 S3 获取图像。
【问题讨论】:
-
也许改用
before_validation?如果记录无效,保存调用将不会运行。 -
@max 看来
before_validation正在产生相同的行为。当s3_key是唯一修改的属性时,回调似乎不会触发。
标签: ruby-on-rails ruby activerecord amazon-s3 paperclip