Rails 4 più immagini o upload di file usando carrierwave

Come posso caricare più immagini da una finestra di selezione file usando Rails 4 e CarrierWave? Ho un modello post_controller e post_attachments . Come posso fare questo?

Qualcuno può fornire un esempio? C’è un approccio semplice a questo?

Questa è la soluzione per caricare più immagini utilizzando carrierwave in rail 4 da zero

Oppure puoi trovare una demo funzionante: Multiple Attachment Rails 4

Per fare basta seguire questi passaggi.

 rails new multiple_image_upload_carrierwave 

Nel file gem

 gem 'carrierwave' bundle install rails generate uploader Avatar 

Crea scaffold post

 rails generate scaffold post title:string 

Crea lo scaffold post_attachment

 rails generate scaffold post_attachment post_id:integer avatar:string rake db:migrate 

In post.rb

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

In post_attachment.rb

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

In 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| %> 
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.fields_for :post_attachments do |p| %>
<%= p.label :avatar %>
<%= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
<% end %>
<%= f.submit %>
<% end %>

Per modificare un allegato e un elenco di allegati per qualsiasi post. In views / posts / show.html.erb

 

<%= notice %>

Title: <%= @post.title %>

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

Aggiorna modulo per modificare le viste di un allegato / post_attachments / _form.html.erb

 <%= image_tag @post_attachment.avatar %> <%= form_for(@post_attachment) do |f| %> 
<%= f.label :avatar %>
<%= f.file_field :avatar %>
<%= f.submit %>
<% end %>

Modifica il metodo di aggiornamento 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 

In rails 3 non è necessario definire parametri forti e come è ansible definire attributo_accessibile sia nel modello che accept_nested_attribute per postare il modello perché l'attributo accessibile è deprecato nelle rotaie 4.

Per modificare un allegato non possiamo modificare tutti gli allegati alla volta. quindi sostituiremo uno per uno gli allegati, oppure puoi modificare secondo la tua regola, qui ti mostrerò come aggiornare qualsiasi allegato.

Se diamo un’occhiata alla documentazione di CarrierWave, questo è in realtà molto semplice ora.

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

Userò il prodotto come modello di cui voglio aggiungere le immagini, ad esempio.

  1. Ottieni il ramo principale Carrierwave e aggiungilo al tuo Gemfile:

     gem 'carrierwave', github:'carrierwaveuploader/carrierwave' 
  2. Creare una colonna nel modello desiderato per ospitare una serie di immagini:

     rails generate migration AddPicturesToProducts pictures:json 
  3. Esegui la migrazione

     bundle exec rake db:migrate 
  4. Aggiungi immagini al modello di prodotto

     app/models/product.rb class Product < ActiveRecord::Base validates :name, presence: true mount_uploaders :pictures, PictureUploader end 
  5. Aggiungi immagini a parametri forti in ProductsController

     app/controllers/products_controller.rb def product_params params.require(:product).permit(:name, pictures: []) end 
  6. Consenti al modulo di accettare più immagini

     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. Nelle tue visualizzazioni, puoi fare riferimento alle immagini che analizzano l'array di immagini:

     @product.pictures[1].url 

Se si scelgono più immagini da una cartella, l'ordine sarà l'esatto ordine con cui le si preleva dall'alto verso il basso.

Alcune aggiunte minori alla risposta SSR :

accept_nested_attributes_for non richiede di cambiare il controller dell’object genitore. Quindi se correggere

 name: "post_attachments[avatar][]" 

a

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

poi tutte queste modifiche del controller come queste diventano ridondanti:

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

Inoltre, devi aggiungere PostAttachment.new al modulo dell’object principale:

In views / posts / _form.html.erb

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

Ciò renderebbe ridondante questo cambiamento nel controllore del genitore:

 @post_attachment = @post.post_attachments.build 

Per maggiori informazioni vedi Campi di rota_per modulo non mostrato, forma annidata

Se utilizzi Rails 5, modifica il valore Rails.application.config.active_record.belongs_to_required_by_default da true a false (in config / initializers / new_framework_defaults.rb) a causa di un bug all’interno di accept_nested_attributes_for (altrimenti accept_nested_attributes_for non funzionerà generalmente sotto Rails 5) .

MODIFICA 1:

Per aggiungere circa distruggi :

Nei modelli / 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?' } %> 

<% end %> <% end %>

In questo modo semplicemente non è necessario avere il controller di un object figlio! Voglio dire, nessun PostAttachmentsController è più necessario. Come per il controllore dell'object genitore ( PostController ), quasi non lo si cambia: l'unica cosa che si modifica è l'elenco dei parametri autorizzati (per includere i parametri relativi all'object figlio) in questo modo:

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

Ecco perché il accepts_nested_attributes_for è così sorprendente.

Inoltre ho capito come aggiornare il caricamento di più file e l’ho anche rifattorizzato un po ‘. Questo codice è mio ma ottieni la deriva.

 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 

Ecco il mio secondo refactoring nel modello:

  1. Sposta i metodi privati ​​per modellare.
  2. Sostituisci @motherboard con self.

controller:

 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 

Nel modello della scheda madre:

 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 

Quando si utilizza l’associazione @post.post_attachments non è necessario impostare post_id .