Rails, Attachment_fu - глубокая копия вложений хранилища базы данных

StackOverflow https://stackoverflow.com/questions/2205110

Вопрос

У меня есть модель, скажем, Attachments , которая использует attachment_fu для приема загружаемых файлов от пользователя.Я хочу "глубоко скопировать" (или, в Ruby-ese, глубоко клонировать) вложение, создав таким образом совершенно новый двоичный объект в таблице "db_files".

Я обнаружил, что это еще не совсем решаемая проблема.Эта публикация в блоге:http://www.williambharding.com/blog/rails/rails-faster-clonecopy-of-attachment_fu-images/

Показывает метод, который предположительно работает для хранилища на основе файловой системы.Для хранилищ на базе базы данных "глубокое копирование" завершается с ошибкой.Создается новое "Вложение", но оно использует уже существующий db_file_id, таким образом выполняя неглубокую копию.

Внутри db_file_backend.rb attachment_fu в файле db_file_backend.rb я вижу метод сохранения:

      # Saves the data to the DbFile model
      def save_to_storage
        if save_attachment?
          (db_file || build_db_file).data = temp_data
          db_file.save!
          self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
        end
        true
      end

Итак, я пытаюсь расшифровать это, и я полагаю, что "build_db_file" - это какое-то магическое сокращение для метапрограммирования Ruby для DbFile.new, хотя я не могу подтвердить это (поиск источника не показывает упоминания об этом, и я не могу найти его в Google).

Я не совсем уверен, что он делает, но моя теория заключается в том, что db_file копируется из исходного объекта obj как часть попытки "Глубокого копирования" (в связанном коде), таким образом, он просто запускает сохранение вместо создания.

Моя первоначальная теория заключалась в том, что родительскому объекту (Attachment) будет присвоено значение "new" при попытке глубокого копирования, поэтому я сделал что-то вроде:

 def save_to_storage
    if save_attachment?
      if self.new_record?
        db_file = DbFile.new :data => temp_data
        self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
      end
    end
    true
  end

На самом деле это отлично работает для клонированных объектов, но, к сожалению, все тесты для обычной загрузки неклонированных файлов завершаются неудачей.Объект вложения создан, но никакие данные не записываются в db_file.Теория заключается в том, что родительский объект сохраняется первым, затем материал db_file записывается позже, таким образом, new_record?возвращает false.

Итак, в качестве эксперимента я решил попробовать:

  def save_to_storage
    if save_attachment?
      if self.new_record?
        db_file = DbFile.new :data => temp_data
        self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
      else
        (db_file || build_db_file).data = temp_data
        db_file.save!
        self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
      #end
    end
    true
  end

Это работает частично - db_file заполняется, но затем я получаю сообщение об ошибке в db_file.save!- говоря, что db_file равен нулю.

Так что я в некотором роде загнан в тупик.Я могу проделать еще несколько проб и ошибок, но на данный момент я достиг своего ограниченного понимания того, как работает этот плагин.Я действительно не ожидал и не хочу тратить на это так много времени, поэтому мне не хочется пытаться исследовать attachment_fu дальше, но, боюсь, мне придется спуститься в кроличью нору, чтобы разобраться в этом.Есть какие-нибудь идеи или раздумья?

Спасибо!!

Это было полезно?

Решение 2

Итак, вместо того, чтобы выяснять, как создать новый db_file (что в нашем конкретном случае расточительно), я просто исправил destroy_file, чтобы удалять db_file только в том случае, если больше нет записей вложений, указывающих на него.Это может быть неуместно, если вы позволяете кому-либо "изменять" вложенный db_file на месте но поскольку мы этого не делаем, это отлично работает.

Technoweenie::AttachmentFu::Backends::DbFileBackend.module_eval do
  protected
  def destroy_file
    if db_file && self.class.count( :conditions =>["id <> ? AND db_file_id = ?", self.id, db_file.id] ) == 0
      db_file.destroy 
    end
  end
end

Другие советы

Это всего лишь частичный ответ, объясняющий build_db_file позвонить

Как вы и подозревали, build_db_file вызов выполняет метод генерируется путем создания belongs_to ассоциация.Ассоциация создается здесь:

def self.included(base) #:nodoc:
   Object.const_set(:DbFile, Class.new(ActiveRecord::Base)) unless Object.const_defined?(:DbFile)
   base.belongs_to  :db_file, :class_name => '::DbFile', :foreign_key => 'db_file_id'
end

Таким образом , (db_file || build_db_file) оператор принимает существующий связанный DbFile объект, или создает новый, если он равен нулю, и присваивает temp_data его двоичному полю datatemp_data вероятно, это массив байтов с данными из формы.

И у меня есть один вопрос (я не могу прокомментировать ваш вопрос) - почему вы не звоните db_file.save! после создания его с помощью

db_file = DbFile.new :data => temp_data

?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top