Как проверить, отправляется ли новый файл с Active Storage в модели?
В моей модели Rails у меня есть этот код для принудительного изменения имени файла при загрузке:
before_save :set_filename
def set_filename
if file.attached?
self.file.blob.update(filename: "#{new_file_name()}.#{self.file.blob.content_type.split('/')[1]}")
end
end
Проблема в том, что имя файла изменяется, даже если новый файл не отправляется в форме (при редактировании).
Мой вложение просто называется файл:
# active storage
has_one_attached :file
Как действительно проверить, что новый файл прилагается при загрузке?
Спасибо,
РЕДАКТИРОВАТЬ: больше разъяснений
У меня есть форма с file_field. Я хочу проверить, отправляется ли новый файл через форму, когда я добавляю или изменяю объект формы.
Моя модель называется Image, а прикрепленный файл называется file.
class Image
has_one_attached :file
end
Я хочу изменить имя файла каждый раз, когда через форму отправляется новый файл, и, конечно же, поле file_field остается пустым.
2 ответа
Вы можете использовать new_record? проверить, если file
новый то есть:
def set_filename
if file.attached? && file.new_record?
self.file.blob.update(filename: "#{new_file_name()}.#{self.file.blob.content_type.split('/')[1]}")
end
end
В качестве альтернативы используйте before_create
вместо before_save
чтобы set_name
запускается только при загрузке нового файла.
обновленный
Интересно, что ActiveStorage обрабатывает изменения больших двоичных объектов вне хуков моделей. По-видимому, сейчас он даже не поддерживает проверку. Нет никакого способа проверить, что блоб изменился, поскольку его состояние нигде не сохраняется. Если вы заглядываете в журнал рельсов, обратите внимание, что рельсы удаляют старый блоб, как только добавляется новый.
Несколько вариантов, которые я могу придумать:
1. Обновить имя файла в контроллере, например:
original_name = params[:file].original_name
params[:file].original_name = # your logic goes here
2. Сохраните имя файла блоба в родительской модели и сравните в before_save
,
def set_filename
if file.attached? && file.blob.filename != self.old_filename
self.file.blob.update(filename: "#{new_file_name()}.#{self.file.blob.content_type.split('/')[1]}")
end
end
Ни одно из этих решений не является идеальным, но надеюсь, что они дадут вам некоторые идеи.
Я только что решил эту проблему (или что-то очень похожее), используя запись, связанную с вложением.
Посколькуblob
являетсяActiveStorage::Blob
, который является производным отActiveRecord::Base
, вы можете использовать для него «грязные» методы, чтобы увидеть, было ли оно изменено. например
def set_filename
if file.attached? && (file_blob.has_changes_to_save? || file_blob.saved_changes?)
self.file.blob.update(filename: "#{new_file_name()}.#{self.file.blob.content_type.split('/')[1]}")
end
end
В зависимости от того, где в жизненном циклеset_filename
вызывается из, может потребоваться только проверка одного изhas_changes_to_save?
илиsaved_changes?
. Поскольку в вашем примере вы вызываете это вbefore_save
, вам понадобится толькоfile_blob.has_changes_to_save?