C'est une solution pour télécharger plusieurs images à l'aide de carrierwave dans les rails 4 à partir de zéro
Ou vous pouvez trouver une démo de travail:
Rails de fixation multiples 4
Pour ce faire, suivez simplement ces étapes.
rails new multiple_image_upload_carrierwave
Dans le fichier gem
gem 'carrierwave'
bundle install
rails generate uploader Avatar
Créer un échafaudage de poste
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 modifier une pièce jointe et une liste de pièces jointes pour n'importe quel 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 les rails 3, il n'est pas nécessaire de définir des paramètres forts et comme vous pouvez définir attribute_accessible à la fois dans le modèle et accept_nested_attribute pour poster le modèle car l'attribut accessible est déconseillé dans les rails 4.
Pour modifier 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 la modifier selon votre règle, ici je vous montre simplement comment mettre à jour une pièce jointe.
create
action? Les rails et carrierwave sont suffisamment intelligents pour enregistrer automatiquement les collections.:_destroy
Si nous jetons un œil à la documentation de CarrierWave, c'est en fait très facile maintenant.
https://github.com/carrierwaveuploader/carrierwave/blob/master/README.md#multiple-file-uploads
J'utiliserai Product comme modèle que je souhaite ajouter les images, à titre d'exemple.
Obtenez la branche principale Carrierwave et ajoutez-la à votre Gemfile:
gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
Créez une colonne dans le modèle prévu pour héberger un tableau d'images:
rails generate migration AddPicturesToProducts pictures:json
Exécutez la migration
bundle exec rake db:migrate
Ajouter des images au modèle de produit
app/models/product.rb class Product < ActiveRecord::Base validates :name, presence: true mount_uploaders :pictures, PictureUploader end
Ajouter des images aux paramètres forts dans ProductsController
app/controllers/products_controller.rb def product_params params.require(:product).permit(:name, pictures: []) end
Autorisez votre formulaire à accepter plusieurs photos
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 %>
Dans vos vues, vous pouvez référencer les images analysant le tableau d'images:
@product.pictures[1].url
Si vous choisissez plusieurs images dans un dossier, l'ordre sera l'ordre exact dans lequel vous les prenez de haut en bas.
la source
UndefinedConversionError ("\x89" from ASCII-8BIT to UTF-8)
pour la solution SSR, cela fonctionne bien avec Rails 4.xx, mais je suis confronté à des défis (avec Rails 5.xx) c'est à dire son stockageActionDispatch::Http::UploadedFile
dans la base de données au lieu du nom de fichier. Il ne stocke pas non plus les fichiers dans les dossiers publics pour un chemin donné dans le programme de téléchargement.Quelques ajouts mineurs à la réponse SSR :
accepte_nested_attributes_for ne vous oblige pas à changer le contrôleur de l'objet parent. Alors si pour 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
Vous devez également ajouter
PostAttachment.new
au formulaire 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:
Pour plus d'informations, consultez les champs Rails_for formulaire non affiché, formulaire imbriqué
Si vous utilisez Rails 5, changez la
Rails.application.config.active_record.belongs_to_required_by_default
valeur detrue
àfalse
(dans config / initializers / new_framework_defaults.rb) en raison d'un bogue dans accepte_nested_attributes_for (sinon, accepte_nested_attributes_for ne fonctionnera généralement pas sous Rails 5).MODIFIER 1:
Pour ajouter à propos de 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 tout simplement pas besoin d'avoir le contrôleur d'un objet enfant! Je veux dire qu'aucun
PostAttachmentsController
n'est plus nécessaire. En ce qui concerne le contrôleur de l' objet parent (PostController
), vous aussi presque ne le changez pas non plus - la seule chose que vous changez ici est la liste des paramètres de la liste blanche (pour inclure les paramètres liés à l'objet 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 si incroyable.la source
<%= d.text_field :copyright, name: "album[diapos_attributes][][copyright]", class: 'form-field' %>
écrit les droits d'auteur uniquement sur le dernier enregistrement et non sur tous.J'ai également compris comment mettre à jour le téléchargement de plusieurs fichiers et je l'ai également refactoré un peu. Ce code est le mien 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
la source
Voici mon deuxième refactor dans le modèle:
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
la source
Lorsque vous utilisez l'association,
@post.post_attachments
vous n'avez pas besoin de définir lepost_id
.la source