Carrierwave kullanarak 4 çoklu görüntü veya dosya yükleme rayları


86

Rails 4 ve CarrierWave kullanarak bir dosya seçim penceresinden birden fazla görüntüyü nasıl yükleyebilirim? Bir post_controllerve post_attachmentsmodelim var. Bunu nasıl yapabilirim?

Birisi bir örnek verebilir mi? Buna basit bir yaklaşım var mı?

Yanıtlar:


195

Bu, taşıyıcı dalgayı kullanarak raylarda 4 sıfırdan birden fazla görüntü yüklemek için bir çözümdür

Veya çalışma demosunu bulabilirsiniz: Multiple Attachment Rails 4

Yapmak için sadece şu adımları izleyin.

rails new multiple_image_upload_carrierwave

Gem dosyasında

gem 'carrierwave'
bundle install
rails generate uploader Avatar 

İskele sonrası oluşturun

rails generate scaffold post title:string

Post_attachment iskele oluştur

rails generate scaffold post_attachment post_id:integer avatar:string

rake db:migrate

Post.rb içinde

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

Post_attachment.rb içinde

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

Post_controller.rb içinde

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

Views / posts / _form.html.erb'de

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

Herhangi bir gönderi için bir eki ve ek listesini düzenlemek için. Views / posts / show.html.erb'de

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

Ek görünümlerini / post_attachments / _form.html.erb düzenlemek için formu güncelleyin

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

Post_attachment_controller.rb'deki güncelleme yöntemini değiştirin

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

Raylar 3'te güçlü parametreler tanımlamanıza gerek yoktur ve hem modelde hem de kabul _nested_ özniteliğinde özellik_accessible tanımlayabildiğiniz için, erişim özelliği raylarda 4 kullanımdan kaldırılmıştır.

Bir eki düzenlemek için bir seferde tüm ekleri değiştiremeyiz. bu yüzden eki birer birer değiştireceğiz veya kuralınıza göre değiştirebilirsiniz, Burada size herhangi bir eki nasıl güncelleyeceğinizi göstereceğim.


2
post kontrolörünün gösteri eyleminde unuttuğunuzu düşünüyorum @post = Post.find (params [: id])
wael

2
@SSR Neden her bir gönderi ekinde çalışırken dönüp duruyorsunuz create? Raylar ve carrierwave, koleksiyonları otomatik olarak kaydedecek kadar akıllıdır.
şahin

3
Düzenlemeyi görmek isterim (özellikle işleme :_destroybölümü)
Tun

5
@SSR - Cevabınız çok yardımcı oldu. Lütfen cevabınızı düzenleme eylemi ile de günceller misiniz?
raj_on_rails

2
Post_attachment modeline doğrulamalar eklediğimde, post modelinin kaydedilmesini engellemiyorlar. Bunun yerine gönderi kaydedilir ve ardından yalnızca ek modeli için ActiveRecord geçersiz hatası atılır. Bunun yaratılıştan kaynaklandığını düşünüyorum! yöntem. ancak bunun yerine create işlevinin kullanılması sessizce başarısız olur. Doğrulamanın eklere ulaşıldığında nasıl yapılacağına dair bir fikriniz var mı?
dchess

32

CarrierWave'in belgelerine bakarsak, bu aslında artık çok kolay.

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

Resimleri eklemek istediğim model olarak Ürünü örnek olarak kullanacağım.

  1. Carrierwave ana dalını edinin ve Gemfile'ınıza ekleyin:

    gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
    
  2. Bir dizi görüntüyü barındırmak için amaçlanan modelde bir sütun oluşturun:

    rails generate migration AddPicturesToProducts pictures:json
    
  3. Taşıma işlemini çalıştırın

    bundle exec rake db:migrate
    
  4. Ürün modeline resim ekle

    app/models/product.rb
    
    class Product < ActiveRecord::Base
      validates :name, presence: true
      mount_uploaders :pictures, PictureUploader
    end
    
  5. ProductsController'daki güçlü parametrelere resim ekleyin

    app/controllers/products_controller.rb
    
    def product_params
      params.require(:product).permit(:name, pictures: [])
    end
    
  6. Formunuzun birden fazla resmi kabul etmesine izin verin

    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. Görünümlerinizde, resimler dizisini ayrıştıran görüntülere başvurabilirsiniz:

    @product.pictures[1].url
    

Bir klasörden birkaç görüntü seçerseniz, sıra, onları yukarıdan aşağıya doğru tam olarak aldığınız sıra olacaktır.


10
CarrierWave'in bu soruna çözümü beni utandırıyor. Dosyalara yapılan tüm referansları bir dizideki tek bir alana koymayı içerir! Kesinlikle "ray yolu" olarak görülmez. Ya daha sonra bazılarını kaldırmak veya gönderiye fazladan dosya eklemek isterseniz? Bunun mümkün olmadığını söylemiyorum, sadece çirkin olacağını söylüyorum. Bir birleştirme masası çok daha iyi bir fikirdir.
Toby 1 Kenobi

3
Daha fazla Toby'ye katılamazdım. Bu çözümü sağlamak için çok nazik davranır mısınız?
drjorgepolanco

2
Bu çözümler zaten SSR tarafından sağlanıyor. Karşıya yüklenen dosyayı tutmak için başka bir model yerleştirilir, daha sonra birçok dosyanın yüklenmesini gerektiren şey, diğer modelle bire çok veya çoka çok ilişkisiyle ilgilidir. (önceki yorumumda bahsettiğim birleştirme tablosu
Toby 1 Kenobi

Teşekkürler @ Toby1Kenobi, sütun dizisi yönteminin görüntü sürümlerini nasıl açıklayacağını merak ediyordum (nasıl olabileceğini bilmiyorum). Stratejiniz yapılabilir.
chaostheory

Carrierwave'in bu özelliğini Rails 5.xx, github.com/carrierwaveuploader/carrierwave/blob/master/… ile uyguladım ancak başarılı bir şekilde çalıştıramıyorum ve hata oluşturuyor, UndefinedConversionError ("\x89" from ASCII-8BIT to UTF-8) SSR çözümü için iyi çalışıyor Rails 4.xx, ancak zorluklarla karşılaşıyorum (Rails 5.xx ile), yani ActionDispatch::Http::UploadedFiledosya adı yerine veritabanında saklanması . Ayrıca, yükleyicide verilen yol için dosyaları ortak klasörlerde depolamaz.
Mansi Shah

8

SSR yanıtına bazı küçük eklemeler :

accepts_nested_attributes_for , üst nesnenin denetleyicisini değiştirmenizi gerektirmez. Yani düzeltmek gerekirse

name: "post_attachments[avatar][]"

-e

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

tüm bu denetleyici değişiklikleri, bunun gibi gereksiz hale gelir:

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

Ayrıca PostAttachment.newana nesne formuna eklemelisiniz :

Views / posts / _form.html.erb'de

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

Bu, ebeveynin denetleyicisindeki bu değişikliği gereksiz hale getirir:

@post_attachment = @post.post_attachments.build

Daha fazla bilgi için bkz. fields_for formunu Raylar görünmüyor, iç içe formu

Eğer Raylar 5 kullanırsanız, o zaman değiştirmek Rails.application.config.active_record.belongs_to_required_by_defaultdeğeri trueiçin false(config / ilklendiriciler / new_framework_defaults.rb) nedeniyle bir hata içine accepts_nested_attributes_for (Aksi accepts_nested_attributes_for olacak Raylar 5 altında olmayan genellikle iş).

DÜZENLEME 1:

Yok etme hakkında eklemek için :

Modellerde / post.rb

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

Views / posts / _form.html.erb'de

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

Bu şekilde , bir alt nesnenin denetleyicisine sahip olmanıza gerek kalmaz ! Demek istediğim artık hiçbir şeye PostAttachmentsControllergerek yok. Üst nesnenin controller ( PostController) 'a gelince , neredeyse değiştirmiyorsunuz - orada değiştirdiğiniz tek şey, beyaz listedeki parametrelerin listesidir (alt nesne ile ilgili parametreleri dahil etmek için):

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

Bu yüzden bu accepts_nested_attributes_forkadar şaşırtıcı.


Bunlar aslında @SSR cevabına yapılan büyük eklemeler, küçük değil :) accept_nested_attributes_for oldukça önemli. Aslında bir alt denetleyiciye hiç gerek yoktur. Yaklaşımınızı izleyerek, yapamadığım tek şey, yüklemede bir şeyler ters gittiğinde çocuk için form hata mesajlarını görüntülemektir.
Luis Fernando Alen

Giriş için teşekkürler. Yüklemeyi çalıştırdım, ancak views / posts / _form.html.erb'deki post_attachments form alanına nasıl ek özellikler ekleyebileceğimi merak ediyorum. <%= d.text_field :copyright, name: "album[diapos_attributes][][copyright]", class: 'form-field' %>telif hakkını hepsine değil, yalnızca son kayda yazar.
SEJU

6

Ayrıca çoklu dosya yüklemesini nasıl güncelleyeceğimi buldum ve biraz da yeniden düzenledim. Bu kod benim ama sürüklenmeyi anlıyorsun.

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

1
Kodunuzu paylaştığınız için teşekkürler. Zamanınız olduğunda lütfen gihub depomdaki kodu güncelleyin ve herkesin kodu kolayca anlayabilmesi için her yöntem için yorum yapmayı unutmayın.
SSR

1
Depoları klonladım, PR yapmak için bana izin verir misin?
Chris Habgood

Evet elbette. Github kullanıcı adınız nedir
SSR

Bana erişim izni verme şansın oldu mu?
Chris Habgood

2

İşte modeldeki ikinci refaktörüm:

  1. Özel yöntemleri modele taşıyın.
  2. @Motherboard'u self ile değiştirin.

Denetleyici:

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

Anakart modelinde:

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

İlişkilendirmeyi kullanırken, @post.post_attachmentsayarlamanıza gerek yoktur post_id.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.