Проблема Rails 3 и Paperclip — как сохранить загруженные файлы в файловую систему?

Я следил за этим руководством, пытаясь интегрировать Paperclip в свое приложение Rails 3. .

Однако мой случай выглядит немного иначе, чем то, что описано в этом уроке.

В моем случае User моделей уже существуют в базе данных, и я хочу загрузить файл(ы) и связать их с загрузчиком.

Вот соответствующие части моего кода:

class User < ActiveRecord::Base
  has_many :assets, :foreign_key => "uploader_id"
end

class Asset < ActiveRecord::Base
  belongs_to :uploader, :class_name => "User"  
  has_attached_file :asset, :styles => { :thumb => "100x100#" }
end

Основное различие между моим случаем и учебным пособием заключается в том, что поле ввода для загрузки не находится внутри формы User:

# views/lounge/index.html.erb
<%= form_tag('/lounge/upload', :multipart => true) do %>
  <input id="uploader_id" name="uploader_id" type="hidden" />
  <%= file_field_tag "assets[]", :multiple => true %>
<% end %>

Значение скрытого поля ввода uploader_id контролируется Javascript.

При отправке формы вызывается метод upload:

class LoungeController < ApplicationController
  def upload
    uploader = User.find(params[:uploader_id])
    # ??
  end
end

Что мне делать с params[:assets], чтобы сохранить загруженные файлы в файловую систему и создать соответствующие модели Asset в базе данных?


person Misha Moroshko    schedule 22.06.2011    source источник


Ответы (1)


Чтобы сохранить файлы, вам нужно создать модели активов, а также назначить прикрепленный_файл этим моделям (которые вы также назвали asset, что может запутать). В простом случае это будет выглядеть так:

user = User.find(id)
asset = user.assets.new
asset.asset = params[:asset]
asset.save

Чтобы сделать несколько файлов, просто выполните быстрый цикл:

user = User.find(id)
params[:assets].each do |asset|
  asset = user.assets.new
  asset.asset = asset
  asset.save
end

Тем не менее, вы делаете здесь ряд нестандартных вещей и усложняете себе жизнь, чем она должна быть. Вместо использования file_field_tag вы должны использовать помощник file_field с активом, созданным в памяти. Например, если вы следовали учебнику, на который вы ссылаетесь, у вас будет что-то вроде:

<% form_for @user do |f|
  <% f.fields_for :assets do |asset| %>
    <%= asset.file_field :asset %>
  <% end %>
<% end %>

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

def User
  accepts_nested_attributes_for :assets
end

Затем в вашем действии контроллера вы просто создадите несколько новых ресурсов в памяти, чтобы цикл в fields_for работал:

def upload
  @user = User.find(id)
  5.times do { @user.assets.build }
end

Надеюсь, это имеет смысл. Продолжайте идти - вы доберетесь туда.

person Frankie Roberto    schedule 22.06.2011
comment
Большое спасибо за ответ! Мне действительно очень помогло! Я не могу реализовать это с помощью пользовательской формы (как это предлагается в учебнике), потому что я заранее не знаю, кто является пользователем, который собирается загрузить файл. Знаю, звучит странно, но это так :) Пара вопросов: (1) В случае, если загружено N файлов, можно ли сохранить их сразу, а не вызывать N раз для save ? (2) (Глупый вопрос) Не могли бы вы уточнить, что делает user.assets.new ? user.assets это Array. Так ты звонишь new на Array?!? Кстати, это работает как шарм :) - person Misha Moroshko; 22.06.2011
comment
1) Возможно, вы сможете опустить строку assets.save и вместо этого вызвать user.save в конце - я не уверен, не проверив. Попробуйте и посмотрите! - person Frankie Roberto; 22.06.2011
comment
2) user.assets - это не массив, это метод, созданный ассоциацией has_many, который возвращает массив. user.assets.new — это другой метод, который возвращает новый объект Asset с установленным user_id. Есть и другие методы: user.assets‹‹(asset) присваивает новый объект, user.assets.size возвращает количество связанных объектов и так далее. См. api.rubyonrails.org/classes/ActiveRecord/Associations. / для полного списка. - person Frankie Roberto; 22.06.2011
comment
PS Мне любопытно - почему вы устанавливаете user_id как скрытое поле через javascript? Звучит как большой риск для безопасности... - person Frankie Roberto; 22.06.2011
comment
Спасибо за все! Я не мог заставить его работать только с одним save. Что касается скрытого поля: в моем приложении пользователи могут входить в систему как группа (например, если они используют один и тот же компьютер и не хотят входить/выходить из системы каждый раз, когда хотят загрузить файл/написать сообщение). Таким образом, через интерфейс приложения они выбирают, кто они (и это меняет значение скрытого поля). Разумеется, я проверяю, что значение скрытого поля принадлежит авторизованной группе. - person Misha Moroshko; 25.06.2011