Rails 3 + JQuery-File-Upload + 嵌套模型

12

我一直在寻找一些例子,但始终没有找到:

我正在尝试在正在开发的项目中实现JQuery-File-Upload,但是对于如何将其与嵌套属性配合使用感到困惑。

快速概述:

2个模型:

Comment [has_many :attachments]
Attachment [belongs_to :comment]

评论模型使用了 :attachments 的 accepts_nested_attributes_for。另外,我正在使用 Dragonfly。

我已经查看了 JQuery-File-Upload 网站上的 Rails 3 指南,但它们假设只有一个模型,所以所有内容都围绕一个表单构建。是否有人有他们实现的示例,或者存在我还没有发现的现有教程?

我相信有人遇到过类似的问题...JQuery-File-Upload 是否是合适的工具,还是我应该看看其他东西?


嘿,我遇到了一个类似的问题,就是无法在nested_form gem中使用JQuery-File-Upload。你能找到解决方法吗? - Engin Erdogan
你特别要求 AJAX 上传,也就是说文件在用户填写表单的同时发送,是吗?你是在处理图片还是一般的文件?此外,Attachment 模型除了文件之外还有其他内容吗,例如验证等? - Andrew
3个回答

6
我想把我的答案放在这里,与Stone的答案一起。我花了将近两天的时间让它工作(Stone说得对,这真是个令人头疼的问题!),所以希望我的解决方案能帮助到某些人。我和Stone的方法略有不同。
我的应用程序有Features(漫画,拼图,文本栏等)和FeatureAssets(单独的漫画面板/颜色版本,特定填字游戏的问答文件等)。由于FeatureAssets仅与一个Feature相关联,所以我将模型嵌套(如您在我的上传表单中所见)。
对我来说最大的问题是意识到发送到服务器的params[:feature_asset]实际上是我的上传者上传的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>

虽然这只是一个最基本的版本,没有错误处理或其他一些好用的功能,但它可以帮助某些人。如果您有任何问题,请随时向我提问!

Kyle


我试图和你以及Stone做同样的事情,但是我需要feature_asset包含在创建@feature的同一表格中。有什么建议吗? - Alain Goldman
@AlainGoldman - 你是想在同一个表单中创建“Feature”和它的“FeatureAssets”吗?这是我从中获取代码的教程和应用程序。它正是如此!https://github.com/tors/jquery-fileupload-rails - Kyle Carlson
我通过使用类似于以下解决方案解决了这个问题:http://www.railscook.com/recipes/multiple-files-upload-with-nested-resource-using-paperclip-in-rails/ - Galaxy

1

我有一个类似的设置,使用Carrierwave运行。这是我的设置。我正在使用图像作为项目的嵌套资源。

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

请注意,这个东西真是让人头疼。

更新: projects/_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>

1
当然,马克。我已经很久没有看过这段代码了,但这是有关图像上传的表单代码。我只是在hidden_field_tag中传递project_id,以便稍后使用。祝你好运! - Stone
PS - 我回头看了一下,可能已经重写了表单代码,使其不再嵌套。无论如何,这就是我现在拥有的。希望额外的信息有所帮助。 - Stone
我明白了。所以你不是同时创建带有图像的项目吗?我正在尝试使用你的示例提交一个包含多个图像的新项目。有什么想法吗? - Marc
我正在尝试做与你相同的事情,但我需要@project包含在创建图像的同一表单中。有什么建议吗? - Alain Goldman
@AlainGoldman - 一定要看看Ryan Bates关于嵌套表单的Railscasts。你应该能够将这个页面上的答案中的信息应用到你的场景中。就像我之前说的,这真是一个巨大的烦恼...最好在早上开始处理这个问题,而不是在经过漫长的一天后晚上再来处理 :) - Stone
显示剩余5条评论

0

我已经解决了这个问题,并制作了一个演示应用程序来展示如何做到这一点。

简而言之,我有两个模型:item和upload。

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>

请注意,仅链接答案是不被鼓励的,SO答案应该是寻找解决方案的终点(而不是另一个参考站点,随着时间的推移往往会变得陈旧)。请考虑在此处添加独立的摘要,将链接保留为参考。 - kleopatra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接