Подтвердить что ты не робот

Не теряйте прикрепление скрепки, когда модель не может быть сохранена из-за ошибки проверки

Сценарий - это нормальная модель, которая содержит вложение скрепки вместе с некоторыми другими столбцами, которые имеют различные проверки. Когда форма для создания объекта не может быть сохранена из-за ошибки проверки, не связанной с вложением, столбцы, такие как строки, сохраняются и остаются предварительно заполненными для пользователя, но файл, выбранный для загрузки, полностью потерян и должен быть повторно выбран пользователем.

Существует ли стандартный подход к сохранению вложения в случае ошибки проверки модели? Это кажется очень распространенным вариантом использования.

Кажется нецелесообразным взломать решение, в котором файл сохраняется без владельца, а затем снова подключается к объекту после его успешного сохранения, поэтому я надеюсь избежать этого.

4b9b3361

Ответ 1

Переключитесь на использование CarrierWave. Я знаю, что это было в комментарии, но я просто потратил весь день на переход, так что мой ответ может быть полезен еще.

Сначала вы можете следить за настройкой несущей волны: http://railscasts.com/episodes/253-carrierwave-file-uploads

Чтобы сохранить это изображение между сообщениями, вам нужно добавить скрытое поле с кешем суффикса:

<%= form_for @user, :html => {:multipart => true} do |f| %>
  <p>
    <label>My Avatar</label>
    <%= f.file_field :avatar %>
    <%= f.hidden_field :avatar_cache %>
  </p>
<% end %>

Для Heroku

И если вы развертываете в Heroku, как я, вам нужно внести некоторые изменения, чтобы заставить его работать, поскольку кэширование работает, временно сохраняя закачки в каталоге public/uploads. Поскольку файловая система только для чтения в Heroku, вам нужно, чтобы она использовала папку tmp вместо этого, и теперь она имеет статические файлы.

Сообщите операторской волне использовать папку tmp для кэширования.

В вашем config/initializers/carrierwave.rb(не стесняйтесь создавать, если нет), добавьте:

CarrierWave.configure do |config|
  config.root = Rails.root.join('tmp')
  config.cache_dir = 'carrierwave'
end

Настроить стойку для загрузки статических файлов из папки tmp/carrierwave

В вашем файле config.ru добавьте:

use Rack::Static, :urls => ['/carrierwave'], :root => 'tmp'

Для примера полнофункционального barebones rails/carrierwave/s3/heroku app, проверьте:

https://github.com/trevorturk/carrierwave-heroku (никакой аффилиации, просто было полезно).

Надеюсь, это поможет!

Ответ 2

Мне пришлось исправить это в недавнем проекте, используя PaperClip. Я пробовал вызывать cache_images(), используя after_validation и before_save в модели, но не смог создать по какой-то причине, что я не могу определить, поэтому я просто вызываю его из контроллера.

Модель:

class Shop < ActiveRecord::Base    
  attr_accessor :logo_cache

  has_attached_file :logo

  def cache_images
    if logo.staged?
      if invalid?
        FileUtils.cp(logo.queued_for_write[:original].path, logo.path(:original))
        @logo_cache = encrypt(logo.path(:original))
      end
    else
      if @logo_cache.present?
        File.open(decrypt(@logo_cache)) {|f| assign_attributes(logo: f)}
      end
    end
  end

  private

  def decrypt(data)
    return '' unless data.present?
    cipher = build_cipher(:decrypt, 'mypassword')
    cipher.update(Base64.urlsafe_decode64(data).unpack('m')[0]) + cipher.final
  end

  def encrypt(data)
    return '' unless data.present?
    cipher = build_cipher(:encrypt, 'mypassword')
    Base64.urlsafe_encode64([cipher.update(data) + cipher.final].pack('m'))
  end

  def build_cipher(type, password)
    cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').send(type)
    cipher.pkcs5_keyivgen(password)
    cipher
  end

end

контроллер:

def create
  @shop = Shop.new(shop_params)
  @shop.user = current_user
  @shop.cache_images

  if @shop.save
    redirect_to account_path, notice: 'Shop created!'
  else
    render :new
  end
end

def update
  @shop = current_user.shop
  @shop.assign_attributes(shop_params)
  @shop.cache_images

  if @shop.save
    redirect_to account_path, notice: 'Shop updated.'
  else
    render :edit
  end
end

Вид:

= f.file_field :logo
= f.hidden_field :logo_cache

- if @shop.logo.file?
  %img{src: @shop.logo.url, alt: ''}

Ответ 3

Следуя идее @galatians, я получил это решение (и работал красиво)

Создал репо к этому примеру: * https://github.com/mariohmol/paperclip-keeponvalidation

  • Первое, что нужно сделать, это поместить некоторые методы в активную запись базы, поэтому каждая модель, использующая приложение, может заставить ее работать.

В config/initializers/active_record.rb

module ActiveRecord
    class Base

    def decrypt(data)
      return '' unless data.present?
      cipher = build_cipher(:decrypt, 'mypassword')
      cipher.update(Base64.urlsafe_decode64(data).unpack('m')[0]) + cipher.final
    end

    def encrypt(data)
      return '' unless data.present?
      cipher = build_cipher(:encrypt, 'mypassword')
      Base64.urlsafe_encode64([cipher.update(data) + cipher.final].pack('m'))
    end

    def build_cipher(type, password)
      cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').send(type)
      cipher.pkcs5_keyivgen(password)
      cipher
    end

    #ex: @avatar_cache = cache_files(avatar,@avatar_cache)
    def cache_files(avatar,avatar_cache)
      if avatar.queued_for_write[:original]
        FileUtils.cp(avatar.queued_for_write[:original].path, avatar.path(:original))
        avatar_cache = encrypt(avatar.path(:original))
      elsif avatar_cache.present?
        File.open(decrypt(avatar_cache)) {|f| assign_attributes(avatar: f)}
      end
      return avatar_cache
    end

    end
end
  1. После этого включите в свою модель и прикрепленное поле код выше

В качестве примера я включил это в /models/users.rb

  has_attached_file :avatar, PaperclipUtils.config
  attr_accessor :avatar_cache
  def cache_images
    @avatar_cache=cache_files(avatar,@avatar_cache)
  end
  1. В вашем контроллере добавьте это, чтобы получить из кеша изображение (непосредственно перед тем, где вы сохранили модель)

    @user.avatar_cache = params [: user] [: avatar_cache]

    @user.cache_images

    @user.save

  2. И, наконец, включите это в ваше мнение, чтобы записать местоположение текущего временного изображения

f.hidden_field: avatar_cache

  1. Если вы хотите показать в поле зрения фактический файл, включите его:
<% if @user.avatar.exists?  %>
<label class="field">Actual Image </label>
  <div class="field file-field">  
      <%= image_tag @user.avatar.url %>
    </div>
<% end %>

Ответ 4

сначала сохраните изображение, чем попробуйте остальные

скажет, что у вас есть пользователь с архивом paperclip:

def update
  @user = current_user
  unless params[:user][:avatar].nil?
    @user.update_attributes(avatar: params[:user][:avatar])
    params[:user].delete :avatar
  end
  if @user.update_attributes(params[:user])
    redirect_to edit_profile_path, notice: 'User was successfully updated.' 
  else
    render action: "edit" 
  end
end

Ответ 5

По состоянию на сентябрь 2013 г. paperclip не намерен "исправлять" потерю прикрепленных файлов после проверки. "Проблема (IMHO) проще и правильнее избежать, чем решить"

https://github.com/thoughtbot/paperclip/issues/72#issuecomment-24072728

Я рассматриваю решение CarrierWave, предложенное ранее в решении Джона Гибба

Ответ 6

Также проверьте refile (более новая опция)

Функции

  • Конфигурируемые бэкенды, файловая система, S3 и т.д.
  • Удобная интеграция с ORM
  • На лету манипулирование изображениями и другими файлами
  • Streaming IO для быстрой и удобной загрузки.
  • Работает через форму redisplays, то есть когда проверки не выполняются, даже на S3
  • Удобные прямые загрузки, даже до S3
  • Поддержка нескольких загрузок файлов

https://gorails.com/episodes/file-uploads-with-refile

Ответ 7

Если изображение не требуется, почему бы не разбить форму на две ступени, первая создаст объект, вторая страница позволит вам добавить дополнительную информацию (например, фотографию).

В качестве альтернативы вы можете подтвердить форму, поскольку пользователь вводит эту информацию, чтобы вам не нужно было отправлять форму, чтобы узнать ваши данные.

Ответ 8

В файле просмотра просто поставьте if условие, которое должно принять только запись, имеющую действительный идентификатор. В моем сценарии это фрагмент кода

            <p>Uploaded files:</p>
            <ul>
                <% @user.org.crew.w9_files.each do |file| %>
                  <% if file.id.present? %>
                    <li> <%= rails code to display value %> </li>
                  <% end %>
                <% end %>
            </ul>