Rails 3 + JQuery-File-Upload + Вложенная модель - PullRequest
12 голосов
/ 20 февраля 2012

Я искал несколько примеров, но у меня не получилось:

Я пытаюсь реализовать JQuery-File-Upload в проекте, над которым я работаю, но теряюсь в том, как заставить его работать с вложенными атрибутами.

Быстрый обзор:

2 модели:

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

Комментарий accept_nested_attributes_for :attachments. Также - я использую Dragonfly.

Я рассмотрел руководства по Rails 3 на сайте JQuery-File-Upload, но они предполагают, что это особая модель, поэтому все построено вокруг формы. У кого-нибудь есть примеры их реализации или есть существующий учебник, с которым я еще не сталкивался?

Я уверен, что у кого-то была похожая проблема ... Подходит ли JQuery-File-Upload к соответствующему инструменту или я должен посмотреть на что-то еще?

Ответы [ 3 ]

6 голосов
/ 19 июня 2013

Я просто хотел бросить здесь свой ответ так же, как и у Стоуна.Я потратил почти два трудных дня, чтобы заставить это работать (Стоун был прав, это была PITA!), Так что, надеюсь, мое решение кому-нибудь поможет.Я сделал это просто прикосновением, отличным от 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

features _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>

В нем нет обработки ошибок или каких-либо тонкостейтак и должно быть, но это босоногая версия.

Надеюсь, это поможет кому-то там.Не стесняйтесь спрашивать меня, если у вас есть какие-либо вопросы!

Кайл

1 голос
/ 19 мая 2012

У меня есть аналогичная установка, работающая с 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

Имейте в виду, это былоa *! @ ^%!чтобы начать работать.

ОБНОВЛЕНИЕ: 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 голосов
/ 06 октября 2013

Я справился с этой проблемой и создал демонстрационное приложение , чтобы показать, как это сделать.

Короче, у меня есть две модели: 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>
...