Как проверить, отправляется ли новый файл с 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?

Другие вопросы по тегам