Rails, Attachment_fu - глубокая копия вложений хранилища базы данных - PullRequest
2 голосов
/ 05 февраля 2010

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

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

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

Внутри файла 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 копируется из исходного объекта как часть попытки «глубокого копирования» (в связанном коде), таким образом, он просто вызывает сохранение вместо создания.

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

 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

Это на самом деле хорошо работает для клонированных объектов, но, к сожалению, все тесты для обычной, не клонированной загрузки файлов не выполняются. Объект Attachment создан, но в 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 ]

1 голос
/ 09 февраля 2010

Это только частичный ответ, объясняющий 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 или создает новый, если он равен nil, и назначает temp_data его двоичному полю data. temp_data - это, вероятно, байтовый массив с данными из формы.

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

db_file = DbFile.new :data => temp_data

0 голосов
/ 13 февраля 2010

Хорошо, так что вместо того, чтобы выяснить, как создать новый файл db_file (который в нашем конкретном случае бесполезен), я просто зачеркнул файл destroy_file, пропатченный обезьяной, чтобы удалить файл db_file только в том случае, если на него больше нет ссылок на вложения. Это может быть неуместно, если вы разрешаете кому-либо «изменять» вложение db_file in situ , но, поскольку мы этого не делаем, это прекрасно работает.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...