Rails 3 + JQuery-File-Upload + Nested Model

前端 未结 3 622
面向向阳花
面向向阳花 2020-12-07 18:12

I\'ve been searching for some examples, but have come up short:

I\'m trying to implement JQuery-File-Upload on a project I\'m working on, but am getting lost as to h

相关标签:
3条回答
  • 2020-12-07 18:34

    I just wanted to throw my answer in here as well as Stone's. I spent nearly two solid days getting this to work (Stone was right, it was a PITA!), so hopefully my solution will help someone. I did it just a touch different than Stone.

    My app has Features (a comic, puzzle, text-column, etc) and FeatureAssets (individual comic panels/color versions, question & answer files for a specific crossword, etc). Since FeatureAssets are solely related to one Feature, I nested the models (as you'll see in my upload form).

    The biggest problem for me was realizing that my params[:feature_asset] that was being sent to the server was actually an array of my uploader'd file objects, instead of just the one I was used to working with. After a bit of fiddling with iterating through each file and creating a FeatureAsset from it, it worked like a charm!

    Hopefully I'll translate this clearly. I'd rather provide a bit too much information than not enough. A little extra context never hurts when you're interpreting someone else's code.

    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
    

    And not that it probably helps that much, but my feature_asset_uploader.rb is below. It's pretty stripped down.

    class FeatureAssetContentUploader < CarrierWave::Uploader::Base
    
      storage :file
    
    end
    

    features _form.html.erb (similar to Stone's, but not quite)

    <%= 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>
    

    It doesn't have error handling or any of the niceties it should have, but that's the barebones version of it.

    Hopefully that helps someone out there. Feel free to ask me if you have any questions!

    Kyle

    0 讨论(0)
  • 2020-12-07 18:40

    I have a similar setup running with Carrierwave. Here's what I have. I'm using Images as a nested resource for Projects.

    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
    

    Be aware, this was a *!@^%! to get working.

    UPDATE: 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>
    
    0 讨论(0)
  • 2020-12-07 18:46

    I have coped with this problem and made a demo app to show how to do this.

    In short I have two models: item and 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>" }
    

    I added uploads_attributes to item controller.

    Now you can add jquery-file-upload form to your view, but there is one problem: it sends each photo in separate requests. So there is my jquery-file-upload initializer, which uploads all photos in one request (creating item model) and then redirecting to the root of your app (you need to use item form):

    <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>
    
    0 讨论(0)
提交回复
热议问题