Rails 4 przesyłanie wielu obrazów lub plików za pomocą carrierwave

Jak mogę przesłać wiele obrazów z okna wyboru plików za pomocą Rails 4 i CarrierWave? Mam model post_controller i post_attachments. Jak mogę to zrobić?

Czy ktoś może podać przykład? Czy istnieje do tego proste podejście?
Author: SeanWM, 2014-01-28

6 answers

Jest to rozwiązanie do przesyłania wielu obrazów za pomocą carrierwave w rails 4 od podstaw

Lub można znaleźć demo pracy : Wiele Szyn Mocujących 4

Aby to zrobić, wykonaj następujące kroki.

rails new multiple_image_upload_carrierwave

W pliku gem

gem 'carrierwave'
bundle install
rails generate uploader Avatar 

Create Post scaffold

rails generate scaffold post title:string

Create post_attachment

rails generate scaffold post_attachment post_id:integer avatar:string

rake db:migrate

W poczta.rb

class Post < ActiveRecord::Base
   has_many :post_attachments
   accepts_nested_attributes_for :post_attachments
end

W post_attachment.rb

class PostAttachment < ActiveRecord::Base
   mount_uploader :avatar, AvatarUploader
   belongs_to :post
end

W post_controller.rb

def show
   @post_attachments = @post.post_attachments.all
end

def new
   @post = Post.new
   @post_attachment = @post.post_attachments.build
end

def create
   @post = Post.new(post_params)

   respond_to do |format|
     if @post.save
       params[:post_attachments]['avatar'].each do |a|
          @post_attachment = @post.post_attachments.create!(:avatar => a)
       end
       format.html { redirect_to @post, notice: 'Post was successfully created.' }
     else
       format.html { render action: 'new' }
     end
   end
 end

 private
   def post_params
      params.require(:post).permit(:title, post_attachments_attributes: [:id, :post_id, :avatar])
   end

In views / posts / _form.html.erb

<%= form_for(@post, :html => { :multipart => true }) do |f| %>
   <div class="field">
     <%= f.label :title %><br>
     <%= f.text_field :title %>
   </div>

   <%= f.fields_for :post_attachments do |p| %>
     <div class="field">
       <%= p.label :avatar %><br>
       <%= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
     </div>
   <% end %>

   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>

Aby edytować załącznik I listę załączników dla dowolnego posta. In views / posts / show.html.erb

<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<% @post_attachments.each do |p| %>
  <%= image_tag p.avatar_url %>
  <%= link_to "Edit Attachment", edit_post_attachment_path(p) %>
<% end %>

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

Update form to edit an attachment views / post_attachments / _form.html.erb

<%= image_tag @post_attachment.avatar %>
<%= form_for(@post_attachment) do |f| %>
  <div class="field">
    <%= f.label :avatar %><br>
    <%= f.file_field :avatar %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Modify Update method in post_attachment_controller.rb

def update
  respond_to do |format|
    if @post_attachment.update(post_attachment_params)
      format.html { redirect_to @post_attachment.post, notice: 'Post attachment was successfully updated.' }
    end 
  end
end

W rails 3 nie ma potrzeby definiowania silnych parametrów i jak można zdefiniować attribute_accessible zarówno w modelu, jak i accept_nested_attribute do modelu post, ponieważ atrybut accessible jest przestarzały w rails 4.

Aby edytować załącznik, nie możemy modyfikować wszystkich załączników na raz. więc będziemy zastępować załącznik jeden po drugim, lub możesz modyfikować zgodnie z Twoją regułą, tutaj po prostu pokażę Ci, jak zaktualizować dowolny załącznik.

 190
Author: SSR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 04:43:49

Jeśli przyjrzymy się dokumentacji CarrierWave ' a, jest to teraz bardzo proste.

Https://github.com/carrierwaveuploader/carrierwave/blob/master/README.md#multiple-file-uploads

Użyję produktu jako modelu, który chcę dodać zdjęcia, jako przykład.

  1. Pobierz master branch Carrier i dodaj go do swojego Gemfile:

    gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
    
  2. Utwórz kolumnę w modelu przeznaczonym do hostowania tablicy obrazy:

    rails generate migration AddPicturesToProducts pictures:json
    
  3. Uruchom migrację

    bundle exec rake db:migrate
    
  4. Dodaj zdjęcia do modelu produktu

    app/models/product.rb
    
    class Product < ActiveRecord::Base
      validates :name, presence: true
      mount_uploaders :pictures, PictureUploader
    end
    
  5. Dodaj zdjęcia do strong params w ProductsController

    app/controllers/products_controller.rb
    
    def product_params
      params.require(:product).permit(:name, pictures: [])
    end
    
  6. Pozwól formularzowi akceptować wiele zdjęć

    app/views/products/new.html.erb
    
    # notice 'html: { multipart: true }'
    <%= form_for @product, html: { multipart: true } do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>
    
      # notice 'multiple: true'
      <%= f.label :pictures %>
      <%= f.file_field :pictures, multiple: true, accept: "image/jpeg, image/jpg, image/gif, image/png" %>
    
      <%= f.submit "Submit" %>
    <% end %>
    
  7. W swoich widokach możesz odwoływać się do obrazów przetwarzających tablicę obrazów:

    @product.pictures[1].url
    

Jeśli wybierzesz kilka obrazów z folderu, kolejność będzie dokładna od góry do dołu.

 30
Author: drjorgepolanco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-16 22:25:58

Kilka drobnych dodatków do SSR odpowiedź:

Accepts_nested_attributes_for nie wymaga zmiany kontrolera obiektu nadrzędnego. Więc jeśli poprawić

name: "post_attachments[avatar][]"

Do

name: "post[post_attachments_attributes][][avatar]"

Wtedy wszystkie te zmiany kontrolera, takie jak te, stają się zbędne:

params[:post_attachments]['avatar'].each do |a|
  @post_attachment = @post.post_attachments.create!(:avatar => a)
end

Należy również dodać PostAttachment.new do formularza obiektu nadrzędnego:

In views / posts / _form.html.erb

  <%= f.fields_for :post_attachments, PostAttachment.new do |ff| %>
    <div class="field">
      <%= ff.label :avatar %><br>
      <%= ff.file_field :avatar, :multiple => true, name: "post[post_attachments_attributes][][avatar]" %>
    </div>
  <% end %>

To sprawiłoby, że ta zmiana w kontroler rodzica:

@post_attachment = @post.post_attachments.build

Aby uzyskać więcej informacji zobacz Rails fields_for form not showing up, nested form

Jeśli używasz Rails 5, Zmień Rails.application.config.active_record.belongs_to_required_by_default wartość z true na false (w config/initializers/new_framework_defaults.rb) z powodu błędu wewnątrz accepts_nested_attributes_for (w przeciwnym razie accepts_nested_attributes_for generalnie nie będzie działać pod Rails 5).

Edytuj 1:

Aby dodać o :

In models / post.rb

class Post < ApplicationRecord
    ...
    accepts_nested_attributes_for :post_attachments, allow_destroy: true
end

In views / posts / _form.html.erb

 <% f.object.post_attachments.each do |post_attachment| %>
    <% if post_attachment.id %>

      <%

      post_attachments_delete_params =
      {
      post:
        {              
          post_attachments_attributes: { id: post_attachment.id, _destroy: true }
        }
      }

      %>

      <%= link_to "Delete", post_path(f.object.id, post_attachments_delete_params), method: :patch, data: { confirm: 'Are you sure?' } %>

      <br><br>
    <% end %>
  <% end %>

W ten sposób po prostu nie musisz mieć kontrolera obiektu potomnego w ogóle! To znaczy, żadne {[12] } nie jest już potrzebne. Jeśli chodzi o kontroler obiektu nadrzędnego (PostController), ty również prawie nie zmieniasz go - jedyną rzeczą, którą tam zmieniasz, jest lista param na białej liście (aby uwzględnić param powiązane z obiektami podrzędnymi), jak to:

def post_params
  params.require(:post).permit(:title, :text, 
    post_attachments_attributes: ["avatar", "@original_filename", "@content_type", "@headers", "_destroy", "id"])
end

Dlatego accepts_nested_attributes_for jest tak niesamowity.

 7
Author: prograils,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:02

Również wymyśliłem, jak zaktualizować przesyłanie wielu plików i trochę go zrefakturowałem. Ten kod jest mój, ale rozumiesz.

def create
  @motherboard = Motherboard.new(motherboard_params)
  if @motherboard.save
    save_attachments if params[:motherboard_attachments]
    redirect_to @motherboard, notice: 'Motherboard was successfully created.'
  else
    render :new
  end
end


def update
  update_attachments if params[:motherboard_attachments]
  if @motherboard.update(motherboard_params)
    redirect_to @motherboard, notice: 'Motherboard was successfully updated.'
  else
   render :edit
  end
end

private
def save_attachments
  params[:motherboard_attachments]['photo'].each do |photo|
    @motherboard_attachment = @motherboard.motherboard_attachments.create!(:photo => photo)
  end
end

 def update_attachments
   @motherboard.motherboard_attachments.each(&:destroy) if @motherboard.motherboard_attachments.present?
   params[:motherboard_attachments]['photo'].each do |photo|
     @motherboard_attachment = @motherboard.motherboard_attachments.create!(:photo => photo)
   end
 end
 6
Author: Chris Habgood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 06:17:03

Oto mój drugi refaktor do modelu:

  1. Przenieś prywatne metody do modelu.
  2. Zastąp @ płytę główną self.

Kontroler:

def create
  @motherboard = Motherboard.new(motherboard_params)

  if @motherboard.save
    @motherboard.save_attachments(params) if params[:motherboard_attachments]
  redirect_to @motherboard, notice: 'Motherboard was successfully created.'
  else
    render :new
  end
end

def update
  @motherboard.update_attachments(params) if params[:motherboard_attachments]
  if @motherboard.update(motherboard_params)
    redirect_to @motherboard, notice: 'Motherboard was successfully updated.'
  else
    render :edit
  end
end

W modelu płyty głównej:

def save_attachments(params)
  params[:motherboard_attachments]['photo'].each do |photo|
    self.motherboard_attachments.create!(:photo => photo)
  end
end

def update_attachments(params)
  self.motherboard_attachments.each(&:destroy) if self.motherboard_attachments.present?
  params[:motherboard_attachments]['photo'].each do |photo|
    self.motherboard_attachments.create!(:photo => photo)
  end
end
 2
Author: Chris Habgood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 19:23:38

Podczas używania asocjacji @post.post_attachments nie musisz ustawiać post_id.

 2
Author: Chris Habgood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-05 17:06:13