web-dev-qa-db-fra.com

Rails 4 images multiples ou un téléchargement de fichier utilisant carrierwave

Comment puis-je télécharger plusieurs images à partir d'une fenêtre de sélection de fichier en utilisant Rails 4 et CarrierWave? J'ai un post_controller et post_attachments modèle. Comment puis-je faire ceci?

Quelqu'un peut-il donner un exemple? Y at-il une approche simple à cela?

86
SSR

Ceci est une solution pour télécharger plusieurs images en utilisant carrierwave dans Rails 4 à partir de zéro

Ou vous pouvez trouver une démo de travail: Plusieurs pièces jointes Rails 4

Pour ce faire, il vous suffit de suivre ces étapes.

Rails new multiple_image_upload_carrierwave

En fichier gem

gem 'carrierwave'
bundle install
Rails generate uploader Avatar 

Créer un échafaudage post

Rails generate scaffold post title:string

Créer un échafaudage post_attachment

Rails generate scaffold post_attachment post_id:integer avatar:string

rake db:migrate

Dans post.rb

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

Dans post_attachment.rb

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

Dans 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

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

Pour éditer une pièce jointe et une liste de pièces jointes pour tout message. Dans 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 %>

Mettre à jour le formulaire pour modifier une pièce jointe 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 %>

Modifier la méthode de mise à jour dans 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

Dans Rails 3 pas besoin de définir des paramètres forts et comme vous pouvez définir attribut_accessible dans le modèle et accept_nested_attribute pour publier un modèle car l'attribut accessible est obsolète dans Rails 4 .

Pour éditer une pièce jointe, nous ne pouvons pas modifier toutes les pièces jointes à la fois. Nous allons donc remplacer la pièce jointe une par une, ou vous pouvez modifier selon votre règle. Ici, je viens de vous montrer comment mettre à jour une pièce jointe.

194
SSR

Si nous regardons la documentation de CarrierWave, c'est très simple maintenant.

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

Je vais utiliser Product comme modèle. Je veux ajouter les images, à titre d'exemple.

  1. Procurez-vous la branche principale Carrierwave et ajoutez-la à votre Gemfile:

    gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
    
  2. Créez une colonne dans le modèle prévu pour héberger un tableau d'images:

    Rails generate migration AddPicturesToProducts pictures:json
    
  3. Lancer la migration

    bundle exec rake db:migrate
    
  4. Ajouter des images au modèle

    app/models/product.rb
    
    class Product < ActiveRecord::Base
      validates :name, presence: true
      mount_uploaders :pictures, PictureUploader
    end
    
  5. Ajouter des images à des paramètres puissants dans ProductsController

    app/controllers/products_controller.rb
    
    def product_params
      params.require(:product).permit(:name, pictures: [])
    end
    
  6. Autorisez votre formulaire à accepter plusieurs images

    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. Dans vos vues, vous pouvez référencer les images en analysant le tableau d'images:

    @product.pictures[1].url
    

Si vous choisissez plusieurs images dans un dossier, l'ordre sera celui dans lequel vous les prenez, de haut en bas.

30
drjorgepolanco

Aussi, j'ai compris comment mettre à jour le téléchargement de plusieurs fichiers et je l'ai aussi refactoré un peu. Ce code est à moi mais vous obtenez la dérive.

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
Chris Habgood

Quelques ajouts mineurs à la réponse [~ # ~] ssr [~ # ~] :

includes_nested_attributes_for ne vous oblige pas à modifier le contrôleur de l'objet parent. Donc si corriger

name: "post_attachments[avatar][]"

à

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

alors tous ces changements de contrôleur comme ceux-ci deviennent redondants:

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

Aussi, vous devriez ajouter PostAttachment.new à la forme d'objet parent:

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

Cela rendrait redondant ce changement dans le contrôleur du parent:

@post_attachment = @post.post_attachments.build

Pour plus d'informations, voir Rails fields_for formulaire n'apparaissant pas, formulaire imbriqué

Si vous utilisez Rails 5, changez alors Rails.application.config.active_record.belongs_to_required_by_default valeur de true à false (dans config/initializers/new_framework_defaults.rb) en raison d’un bogue à l'intérieur acceptés_nested_attributes_for (sinon includes_nested_attributes_for ne fonctionnera généralement pas sous Rails 5).

EDIT 1:

Pour ajouter environ détruire :

Dans models/post.rb

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

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

De cette façon, vous n'avez simplement pas besoin d'avoir le contrôleur d'un objet enfant! Je veux dire qu'il n'est plus nécessaire d'utiliser PostAttachmentsController. En ce qui concerne le contrôleur de l’objet parent (PostController), vous aussi presque ne le modifiez pas - la seule chose que vous modifiez est la liste des paramètres de la liste blanche (pour inclure le paramètres relatifs aux objets enfant) comme ceci:

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

C'est pourquoi le accepts_nested_attributes_for est tellement incroyable.

6
prograils

Voici mon deuxième refactor dans le modèle:

  1. Déplacer des méthodes privées pour modéliser.
  2. Remplacez la carte mère par self.

Manette:

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

Dans le modèle de carte mère:

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
Chris Habgood

Lors de l'utilisation de l'association @post.post_attachments vous n'avez pas besoin de définir le post_id.

2
Chris Habgood