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

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

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

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

Ответы [ 8 ]

12 голосов
/ 15 апреля 2011

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

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

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

<%= 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 и использовать для хранения статические файлы в стойке.

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

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

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

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

Inдобавьте в свой файл config.ru:

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

В качестве примера полнофункционального приложения barebones rails / carrierwave / s3 / heroku:

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

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

3 голосов
/ 21 января 2015

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

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

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

В 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
После этого включите в вашу модель и прилагаемое поле приведенный выше код

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

  has_attached_file :avatar, PaperclipUtils.config
  attr_accessor :avatar_cache
  def cache_images
    @avatar_cache=cache_files(avatar,@avatar_cache)
  end

В вашем контроллере добавьте это, чтобы получить из кэша изображение (непосредственно перед точкой сохранения модели)

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

@user.cache_images

@user.save

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

f.hidden_field: avatar_cache

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

Мне пришлось исправить это в недавнем проекте с использованием 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: ''}
1 голос
/ 15 июня 2015

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

Возможности :

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

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

1 голос
/ 24 февраля 2014

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

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

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

0 голосов
/ 14 июля 2017

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

            <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>
0 голосов
/ 17 октября 2012

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

Допустим, у вас есть пользователь с аватаркой для скрепки:

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
0 голосов
/ 04 марта 2011

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

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

...