Rails 3 + JQuery-File-Upload + Вложенная модель
Я искал несколько примеров, но немного разобрался:
Я пытаюсь реализовать JQuery-File-Upload в проекте, над которым я работаю, но теряюсь в том, как заставить его работать с вложенными атрибутами.
Краткий обзор:
2 Модели:
Comment [has_many :attachments]
Attachment [belongs_to :comment]
Комментарий accepts_nested_attributes_for :attachments
. Также - я использую Dragonfly.
Я просмотрел руководства Rails 3 на сайте JQuery-File-Upload, но они считают его уникальной моделью, поэтому все это построено вокруг формы. Кто-нибудь есть примеры их реализации или есть ли существующий учебник, который я еще не наткнулся?
Я уверен, что у кого-то была аналогичная проблема... есть JQuery-File-Upload для соответствующего инструмента или я должен смотреть на что-то еще?
Ответы
Ответ 1
Я просто хотел бросить свой ответ здесь, а также Стоуна. Я потратил почти два дня, чтобы это работало (Камень был прав, это был PITA!), Поэтому, надеюсь, мое решение поможет кому-то. Я делал это только прикосновение, отличное от Стоуна.
В моем приложении есть Features
(комикс, головоломка, текстовый столбец и т.д.) и FeatureAssets
(отдельные комиксы/цветовые версии, файлы вопросов и ответов для определенного кроссворда и т.д.). Поскольку FeatureAssets
связаны исключительно с одним Feature
, я вложил в него модели (как вы увидите в моей форме загрузки).
Самая большая проблема для меня заключалась в понимании того, что мой params[:feature_asset]
, который был отправлен на сервер, был фактически массивом объектов uploader'd file
, а не только того, с которым я работал. После немного возиться с итерацией через каждый файл и созданием FeatureAsset от него он работал как шарм!
Надеюсь, я переведу это ясно. Я бы предпочел предоставить слишком много информации, чем недостаточно. Небольшой дополнительный контекст никогда не болит, когда вы интерпретируете код другого пользователя.
feature.rb
class Feature < ActiveRecord::Base
belongs_to :user
has_many :feature_assets
attr_accessible :name, :description, :user_id, :image
accepts_nested_attributes_for :feature_assets, :allow_destroy => true
validates :name, :presence => true
validates :user_id, :presence => true
mount_uploader :image, FeatureImageUploader
end
feature_asset.rb
belongs_to :user
belongs_to :feature
attr_accessible :user_id, :feature_id, :file, :file_cache
validates :user_id, :presence => true
validates :feature_id, :presence => true
validates :file, :presence => true
mount_uploader :file, FeatureAssetContentUploader
# grabs useful file attributes & sends them as JSON to the jQuery file uploader
def to_jq_upload
{
"file" => file,
"file_name" => 'asdf',
"url" => file.url,
"delete_url" => id,
"delete_type" => "DELETE"
}
end
feature_assets_controller.rb
def create
@feature = Feature.find(params[:feature_id])
params[:feature_asset]['file'].each do |f|
@feature_asset = FeatureAsset.create!(:file => f, :feature_id => @feature.id, :user_id => current_user.id)
end
redirect_to @feature
end
И не то, что это, вероятно, так помогает, но мой feature_asset_uploader.rb ниже. Это довольно урезано.
class FeatureAssetContentUploader < CarrierWave::Uploader::Base
storage :file
end
показывает _form.html.erb(аналогично Stone, но не совсем)
<%= form_for [@feature, @feature_asset], :html => { :multipart => true } do |f| %>
<div class="row" id="fileupload">
<div class=" fileupload-buttonbar">
<div class="progressbar fileupload-progressbar nofade"><div style="width:0%;"></div></div>
<span class="btn btn-primary fileinput-button">
<i class="icon-plus"></i>
<span><%= t('feature_assets.add_files') %>...</span>
<%= hidden_field_tag :feature_id, @feature.id %>
<%= hidden_field_tag :user_id, current_user.id %>
<%= f.file_field :file, :multiple => true %>
</span>
<button type="submit" class="btn btn-success">Start Upload</button>
<button type="reset" class="btn btn-warning">Cancel Upload</button>
<button type="button" class="btn btn-danger">Delete Files</button>
</div>
</div>
У него нет обработки ошибок или каких-либо тонкостей, которые он должен иметь, но это его версия на основе barebones.
Надеюсь, это поможет кому-то. Не стесняйтесь спрашивать меня, есть ли у вас какие-либо вопросы!
Кайл
Ответ 2
У меня аналогичная настройка работает с Carrierwave. Вот что у меня есть. Я использую Images как вложенный ресурс для проектов.
Project.rb:
has_many :images, :dependent => :destroy
accepts_nested_attributes_for :images, :allow_destroy => true
Image.rb:
include Rails.application.routes.url_helpers
mount_uploader :image, ImageUploader
belongs_to :project
#one convenient method to pass jq_upload the necessary information
def to_jq_upload
{
"name" => read_attribute(:image),
"size" => image.size,
"url" => image.url,
"thumbnail_url" => image.thumb.url,
"delete_url" => image_path(:id => id),
"delete_type" => "DELETE"
}
end
Images_controller.rb:
def create
@image = Image.new(params[:image])
@image.project_id = params[:project_id]
@project = Project.find(params[:project_id])
@image.position = @project.images.count + 1
if @image.save
render :json => [ @image.to_jq_upload ].to_json
else
render :json => [ @image.to_jq_upload.merge({ :error => "custom_failure" }) ].to_json
end
end
Знайте, это был *! @^%! для работы.
UPDATE:
проекты /_form.html.erb
<div id="fileupload" class="image_add">
<%= form_for Image.new, :html => {:multipart => true} do |f| %>
<div class="fileupload-buttonbar">
<label class="fileinput-button">
<span>Add files...</span>
<%= hidden_field_tag :project_id, @project.id %>
<%= f.file_field :image %>
</label>
<button type="submit" class="start">Start Upload</button>
<button type="reset" class="cancel">Cancel Upload</button>
<button type="button" class="delete">Delete Files</button>
</div>
<% end %>
<div class="fileupload-content">
<div class="dropzone-container">
<div class="dropzone">Drop Image Files Here</div>
</div>
<table class="files"></table>
</div>
</div>
Ответ 3
Я справился с этой проблемой и сделал демонстрационное приложение , чтобы показать, как это сделать.
Короче, у меня есть две модели: элемент и загрузка.
item.rb:
has_many :uploads
accepts_nested_attributes_for :uploads, :allow_destroy => true
upload.rb:
belongs_to :item
has_attached_file :upload, :styles => { :large => "800x800", :medium => "400x400>", :small => "200x200>" }
Я добавил uploads_attributes
в контроллер элемента.
Теперь вы можете добавить форму jquery-file-upload в свое представление, но есть одна проблема: она отправляет каждую фотографию в отдельные запросы. Итак, есть мой инициализатор загрузки jquery-file-upload, который загружает все фотографии в один запрос (создание модели элементов), а затем перенаправляется в корень вашего приложения (вам нужно использовать форму элемента):
<script type="text/javascript" charset="utf-8">
var num_added = 0;
var added = 0;
var all_data = {};
$(function () {
// Initialize the jQuery File Upload widget:
$('#fileupload').fileupload({
complete: function (e, data) {
window.location = "<%= root_url %>";
},
singleFileUploads: false
}) .bind('fileuploadadd', function (e, data) {num_added++;})
.bind('fileuploadsubmit', function (e, data) {
if(added < num_added)
{
if (added == 0)
all_data = data;
else
{
$.each(data['files'], function(i, file){
all_data['files'].push(file);
});
$.each(data['context'], function(i, context){
all_data['context'].push(context);
});
}
added++;
if (added == num_added)
{
added++;
all_data.submit();
}
return false;
}
})
.bind('fileuploadsend', function (e, data) {num_added = 0; added = 0;});
//
// Load existing files:
$.getJSON($('#fileupload').prop('action'), function (files) {
var fu = $('#fileupload').data('blueimpFileupload'),
template;
fu._adjustMaxNumberOfFiles(-files.length);
console.log(files);
template = fu._renderDownload(files)
.appendTo($('#fileupload .files'));
// Force reflow:
fu._reflow = fu._transition && template.length &&
template[0].offsetWidth;
template.addClass('in');
$('#loading').remove();
});
});
</script>