Dan*_*pin 6 ruby-on-rails rails-activestorage
我有一个 Rails 模型:
has_many_attached :files
Run Code Online (Sandbox Code Playgroud)
默认情况下,通过 Active Storage 上传时,如果您上传新文件,它会删除所有现有上传内容并将其替换为新文件。
我有一个控制器破解,由于多种原因,它不太理想:
在 Rails 6 中使用 has_many_attached 更新图像的正确方法是什么
有没有办法配置 Active Storage 以保留现有的存储?
eda*_*edl 20
看起来有一个配置完全可以做到这一点
config.active_storage.replace_on_assign_to_many = false
Run Code Online (Sandbox Code Playgroud)
不幸的是,根据当前的 Rails 源代码,它已被弃用,并将在 Rails 7.1 中删除
config.active_storage.replace_on_assign_to_many已弃用并将在 Rails 7.1 中删除。在升级之前,请确保您的代码可以很好地config.active_storage.replace_on_assign_to_many设置为true。要将新的可附加项附加到 Active Storage 关联,最好使用attach. 使用关联设置器将导致清除现有附加附件并用新附件替换它们。
看起来明确使用attachwill 是唯一的出路。
所以一种方法是在控制器中设置所有内容:
def update
...
if model.update(model_params)
model.files.attach(params[:model][:files]) if params.dig(:model, :files).present?
else
...
end
end
Run Code Online (Sandbox Code Playgroud)
如果您不喜欢在控制器中包含此代码。例如,您可以覆盖模型的默认设置器,如下所示:
class Model < ApplicationModel
has_many_attached :files
def files=(attachables)
files.attach(attachables)
end
end
Run Code Online (Sandbox Code Playgroud)
不确定我是否会建议这个解决方案。我更愿意添加新方法来附加文件:
class Model < ApplicationModel
has_many_attached :files
def append_files=(attachables)
files.attach(attachables)
end
end
Run Code Online (Sandbox Code Playgroud)
并在你的表格中使用
<%= f.file_field :append_files %>
Run Code Online (Sandbox Code Playgroud)
它可能还需要模型中的阅读器,可能还需要一个更好的名称,但它应该演示这个概念。
@edariedl 建议的覆盖 writer 的解决方案不起作用,因为它会导致stack level too deep
您可以像这样覆盖 writer has_many_attached:
class Model < ApplicationModel
has_many_attached :files
def files=(attachables)
attachables = Array(attachables).compact_blank
if attachables.any?
attachment_changes["files"] =
ActiveStorage::Attached::Changes::CreateMany.new("files", self, files.blobs + attachables)
end
end
end
Run Code Online (Sandbox Code Playgroud)
您可以创建一个模型关注点,它将封装所有这些逻辑并使其更加动态,允许您指定has_many_attached您想要旧行为的字段,同时仍然保持新has_many_attached字段的新行为(如果您添加任何后续内容)您启用新的行为。
在app/models/concerns/append_to_has_many_attached.rb
module AppendToHasManyAttached
def self.[](fields)
Module.new do
extend ActiveSupport::Concern
fields = Array(fields).compact_blank # will always return an array ( worst case is an empty array)
fields.each do |field|
field = field.to_s # We need the string version
define_method :"#{field}=" do |attachables|
attachables = Array(attachables).compact_blank
if attachables.any?
attachment_changes[field] =
ActiveStorage::Attached::Changes::CreateMany.new(field, self, public_send(field).public_send(:blobs) + attachables)
end
end
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
在你的模型中:
class Model < ApplicationModel
include AppendToHasManyAttached['files'] # you can include it before or after, order does not matter, explanation below
has_many_attached :files
end
Run Code Online (Sandbox Code Playgroud)
prepend注意:您或include模块并不重要,因为 ActiveStorage 生成的方法已添加到此生成的模块中,当您从此处继承时,该模块会很早就被调用ActiveRecord::Base
==> 所以你的作家将永远优先。
如果您想要更加动态和健壮的东西,您仍然可以创建模型关注点,但您attachment_reflections可以像这样在模型内部循环:
reflection_names = Model.reflect_on_all_attachments.filter { _1.macro == :has_many_attached }.map { _1.name.to_s } # we filter to exclude `has_one_attached` fields
# => returns ['files']
reflection_names.each do |name|
define_method :"#{name}=" do |attachables|
# ....
end
end
Run Code Online (Sandbox Code Playgroud)
但是我相信要使其工作,您需要在对您的所有调用之后包含此模块,has_many_attached否则它将无法工作,因为反射数组不会完全填充(每次调用 has_many_attached 都会附加到该数组)
| 归档时间: |
|
| 查看次数: |
3500 次 |
| 最近记录: |