Panda Ninja Panda Ninja - 2 months ago 6
Ruby Question

Ruby on Rails multiple images connected to one object

I've been trying to create a form that would get parameters for multiple models. I have a photo model that belongs to a product model and I want to make it so that when you create a new product you can also upload images that are linked to that product by id.



<%= form_for @product, html:{multipart:true} do |f| %>
<div class="field">
<%= f.label :price %>
<%= f.text_field :price %>
</div>
<%=form_for @photo do |t| %>
<%t.productID = f.id%>
<div class="field">
<%= t.label (:image) %>
<%= t.file_field (:image) %>
</div>
<%end%>
<div class="actions">
<%= f.submit %>
</div>
<%end%>




right now I'm using paperclip for image attachments and the photo model accepts the images as parameters. I've used paperclip before but the product could only have one image connected to it. If I use the form above I get "First argument in form cannot contain nil or be empty" error and it points to where the form_for @photo starts.I have controllers for both with the usual methods of new, create, update, etc. I've routed resources to both product and photos but I'm still pretty new to rails and don't fully understand how this stuff works.

Answer

I think what you're trying to do is a good application for nested forms using the fields_for helper.

First, you'll need to ensure that your product model and photo model have the right associations (A product probably has_many photos, and a photo belongs to a product, right?). Then you'll make sure the product class 'accepts nested attributes for photo's which allows you to add attributes to the photos model from a products form.

in products.rb

class Product
  has_many :photos
  accepts_nested_attributes_for :photos
end

and in photo.rb

class Photo
  belongs_to :product
end

Then you'll want to make sure any attributes you need for the photo are white-listed in your product params.

in products_controller.rb

private
def product_params
  params.require(product).permit(:first_product_attribute, :second_produtc_attribute, photo_attributes: [:image])
end

Last, you'll create the form using the special helper fields_for

in your view

<%= form_for @product, html:{multipart:true} do |f| %>
  <div class="field">
    <%= f.label :price %>
    <%= f.text_field :price %>
  </div>
  <%= f.fields_for :photo do |t| %>
    <div>
      <%= t.label :image %>
      <%= t.file_field :image, :multiple => true %>
    </div>
  <% end %>
  <div class="actions">
    <%= f.submit %>
  </div>
<%end%>

You'll also need to make sure you're actually creating new photo objects in your product's create action:

in products_controller.rb

def create
  @product = Product.new(product_params)
    if @product.save!
      params[:photo]['image'].each do |img|
        @photo = @product.photos.create!(:image => img)
      end
    flash[:success] = 'product saved!'
    redirect_to @product
  end
end

Some of this is based on my experience doing the same thing but with Carrierwave instead of Paperclip so your specific implementation might be a little different.