在嵌套的form_for中使用jQuery文件上传插件

时间:2013-07-02 19:37:13

标签: jquery ruby-on-rails json ruby-on-rails-3 file-upload

我有一个创建产品页面,里面有嵌套的创建照片。照片属于产品,产品有很多照片。我正在使用the jQuery file upload plugin,但要求您在上传后点击保存。这是一个问题,因为它是我的产品形式,我无法保存没有产品的照片。所以当我点击保存时会发生这种情况。 “内部服务器错误!” (上面创建产品)

The error I see

这就是在控制台输出的内容!

控制台错误

 Started POST "/products" for 127.0.0.1 at 2013-07-02 15:01:38 -0400
Processing by ProductsController#create as JSON
Parameters: {"utf8"=>"✓", "authenticity_token"=>"z5BnHkHTqPZGKt+uWQs10wUEKkGlphXth0rqM82tjR0=", "product"=>{"name"=>"", "description"=>"", "condition"=>"", "quantity"=>"", "price"=>"", "ship_method"=>"", "ship_price"=>"", "photos_attributes"=>{"0"=>{"image"=>#<ActionDispatch::Http::UploadedFile:0x9699c3c @original_filename="eye.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"product[photos_attributes][0][image]\"; filename=\"eye.jpg\"\r\nContent-Type: image/jpeg\r\n", @tempfile=#<File:/tmp/RackMultipart20130702-5814-cu2g3o>>}}}}
User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."auth_token" = 'eQOQkRRSnzfA51iiDQ-90w' LIMIT 1
Command :: identify -format '%wx%h,%[exif:orientation]' '/tmp/eye20130702-5814-12toyjt.jpg[0]'
Command :: identify -format %m '/tmp/eye20130702-5814-12toyjt.jpg[0]'
Command :: identify -format %m '/tmp/eye20130702-5814-12toyjt.jpg[0]'
Command :: identify -format %m '/tmp/eye20130702-5814-12toyjt.jpg[0]'
Command :: convert '/tmp/eye20130702-5814-12toyjt.jpg[0]' -auto-orient -resize "320x240>" '/tmp/eye20130702-5814-12toyjt20130702-5814-vpw0ed'
Command :: file -b --mime '/tmp/eye20130702-5814-12toyjt20130702-5814-vpw0ed'
Command :: identify -format '%wx%h,%[exif:orientation]' '/tmp/eye20130702-5814-12toyjt.jpg[0]'
Command :: identify -format %m '/tmp/eye20130702-5814-12toyjt.jpg[0]'
Command :: identify -format %m '/tmp/eye20130702-5814-12toyjt.jpg[0]'
Command :: identify -format %m '/tmp/eye20130702-5814-12toyjt.jpg[0]'
Command :: convert '/tmp/eye20130702-5814-12toyjt.jpg[0]' -auto-orient -resize "100x100>" '/tmp/eye20130702-5814-12toyjt20130702-5814-b835qy'
Command :: file -b --mime '/tmp/eye20130702-5814-12toyjt20130702-5814-b835qy'
 Rendered products/new.js.erb (5.2ms)
Completed 500 Internal Server Error in 806ms

AbstractController::DoubleRenderError (Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".):
app/controllers/products_controller.rb:34:in `block (2 levels) in create'
app/controllers/products_controller.rb:32:in `create'


Rendered /home/alain/.rvm/gems/ruby-1.9.3-head/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/_trace.erb (39.6ms)
Rendered /home/alain/.rvm/gems/ruby-1.9.3-head/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (2.2ms)
Rendered /home/alain/.rvm/gems/ruby-1.9.3-head/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb   within rescues/layout (52.8ms)

新产品页面(HAML)

= form_for @product,:url => products_path, :html => { :id => "fileupload", :multipart => true } do |f| 

  %p
    = f.label :name
    = f.text_field :name
  %p
    = f.label :description
    = f.text_field :description
  %p
    = f.label :condition
    = f.text_field :condition
  %p
    = f.select :quantity, [['Quantity', nil], '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
  %p
    = f.label :price
    = f.text_field :price, class: "auto", data: { a_sign: "$ " } 
  %p
    =f.select :ship_method, [['Shipping', nil], 'usps', 'ups', 'fedex']
  %p
    = f.label :ship_price
    = f.text_field :ship_price, class: "auto", data: { a_sign: "$ " } 

  %p
    = f.fields_for :photos do  |fp| 
      =fp.file_field :image
      %br

  .files{"data-target" => "#modal-gallery", "data-toggle" => "modal-gallery"}
  %p.button
    = f.submit

:javascript
  var fileUploadErrors = {
  maxFileSize: 'File is too big',
  minFileSize: 'File is too small',
  acceptFileTypes: 'Filetype not allowed',
  maxNumberOfFiles: 'Max number of files exceeded',
  uploadedBytes: 'Uploaded bytes exceed file size',
  emptyResult: 'Empty file upload result'
  };


/ The template to display files available for upload
%script#template-upload{:type => "text/x-tmpl"}
  {% for (var i=0, file; file=o.files[i]; i++) { %}
  <tr class="template-upload fade">
    <td class="preview"><span class="fade"></span></td>
    <td class="name"><span>{%=file.name%}</span></td>
    <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
    {% if (file.error) { %}
    <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
    {% } else if (o.files.valid && !i) { %}
    <td>
      <div class="progress progress-success progress-striped active"><div class="bar" style="width:0%;"></div></div>
    </td>
    <td class="start">{% if (!o.options.autoUpload) { %}
      <button class="btn btn-primary">
        <i class="icon-upload icon-white"></i>
        <span>{%=locale.fileupload.start%}</span>
      </button>
      {% } %}</td>
    {% } else { %}
    <td colspan="2"></td>
    {% } %}
    <td class="cancel">{% if (!i) { %}
      <button class="btn btn-warning">
        <i class="icon-ban-circle icon-white"></i>
        <span>{%=locale.fileupload.cancel%}</span>
      </button>
      {% } %}</td>
  </tr>
  {% } %}
/ The template to display files available for download
%script#template-download{:type => "text/x-tmpl"}
  {% for (var i=0, file; file=o.files[i]; i++) { %}
    <tr class="template-download fade">
      {% if (file.error) { %}
        <td></td>
        <td class="name"><span>{%=file.name%}</span></td>
        <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
        <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[    file.error] || file.error%}</td>
        {% } else { %}
        <td class="preview">{% if (file.thumbnail_url) { %}
          <a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
          {% } %}</td>
        <td class="name">
          <a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
        </td>
        <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
        <td colspan="2"></td>
        {% } %}
      <td class="delete">
        <button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
          <i class="icon-trash icon-white"></i>
          <span>{%=locale.fileupload.destroy%}</span>
        </button>
        <input type="checkbox" name="delete" value="1">
      </td>
    </tr>
    {% } %}
%script{:charset => "utf-8", :type => "text/javascript"}
  $(function () {
      // Initialize the jQuery File Upload widget:
      $('#fileupload').fileupload();
      // 
      // 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();
      });

  });

产品控制器

  def new 
    @product = Product.new
    @photo = Photo.new
    @product.photos.build
  end

  def create
  @product = current_user.products.new(params[:product])
  @photo = current_user.photos.new(params[:photo])

    if @product.valid?
      @product.save
      @photo.product_id = @product.id
      @photo.save
      render "show", :notice => "Sale created!"
    else
      @product.photos.build
      render "new", :notice => "Somehting went wrong!"
    end


    respond_to do |format|
      format.html 
      format.json { render json: @photo }
    end
  end

我应该在世界上做什么?

3 个答案:

答案 0 :(得分:1)

避免复杂化,在创建产品时不显示上传字段,仅在编辑页面显示。

另一种选择是首先通过AJAX发送产品表格,并在AJAX成功时显示图片上传字段。

答案 1 :(得分:1)

我同意@ jjay225在单个请求中上传产品和图片。

但您不必单独保存它们。您可以将它们保存在一个保存命令中。

为此,您可以使用 accepts_nested_attributes_for 。 这样做的好处就是验证。如果一个记录的保存失败,另一个记录也会失败。

如果你需要一个例子,请告诉我。

答案 2 :(得分:0)

我会说浏览图片但尚未上传。然后输入您的产品详细信息并将其发布到服务器,当然会在保存图像链接之前将ID添加到新创建的产品中,以便将图像链接到刚保存的产品。