2
votes

Im creating a website where a User can post an ad of a Property , which can have many Photos .

I have been on this problem for 2 days now , none of the answers I've found have worked for this problem.

Heres the main program from where the issue is I believe . I have tested many different versions from this code and none have been successful .

The params hash in the console returns correct values , its just the manipulation of it causes the problems .

{"utf8"=>"✓", "authenticity_token"=>"3OWguFpgwOYaLmQ4szRmPK/i13kLRr4XT1hcU6YEVlgml1iK1OzAg5c9UyETK1MiqdpoHYLcDGgx4aGd/FaZJg==",

"property"=>{ "title"=>"asda", "price"=>"3", "numberOfBeds"=>"4", "toilets"=>"34", "description"=>"salads", "photo"=>{"property_pic"=>#, @original_filename="ASC_0321.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"property[photo][property_pic]\"; filename=\"ASC_0321.jpg\"\r\nContent-Type: image/jpeg\r\n">}}, "commit"=>"Create Ad"}

property.rb

    class Property < ActiveRecord::Base
      belongs_to :user
      has_many :photos  , dependent: :destroy
      accepts_nested_attributes_for :photos , reject_if: lambda  {|t| t['photo'].nil? 
}
    end

properties_controller.rb

  def new
    @property = Property.new
    @photo  = @property.photos.new
  end

  def create

     @property  = current_user.properties.new(params[:property])

     @property.photos.new(params[[:photo][:property_pic]])
    if @property.save
      redirect_to property_path @property
    else
      render :edit
    end

  end

 def property_params
    params.require(:property).permit(:title,:price,:numberOfBeds,:toilets,:description , photo:[:property_pic])
  end

properties/new.html.erb

<div class="wrapper_skinny">

  <%= form_for @property do |p| %>

      <%= p.label :title %>
      <%= p.text_field :title %> <br><br>

      <%= p.label :price %>
      <%= p.number_field :price %> <br><br>

      <%= p.label :numberOfBeds %>
      <%= p.number_field :numberOfBeds %> <br><br>

      <%= p.label :toilets %>
      <%= p.number_field :toilets %> <br><br>

      <%= p.label :description %>
      <%= p.text_area :description %> <br><br>

      <%= p.fields_for :photo  do |builder|  %>
          <%= builder.label :property_pic %>
          <%= builder.file_field :property_pic %>
      <% end %> <br><br>


      <%= p.submit "Create Ad" , class:"button button-chosen"%>
  <% end %>

</div>

Thanks!

2
Have you tried renaming the photo: in your property_params method to photos_attributes:? That could be the cause of some issues. - Zoran
hi @Zoran , thanks for the comment , I still get unpermitted parameter photo , although with the adjustment i just made , the program doesn't crash when i submit my form . - stringRay2014
Also, there should be <%= p.fields_for :photos %>. Association name needs to match. You will probably notice that all your fields for photos are gone after that change, this is correct and it means it works. Just add @property.photos.build in your controller and all should work. - BroiSatse
where should @property.photos.build be added ? will i add this line instead of this >> @property.photos.new(params[[:photo][:property_pic]]) @BroiSatse - stringRay2014
When you use accepts_nested_attributes_for you do not build it yourself. You just pass it in with a hash called others_attributes. - Lanny Bose

2 Answers

2
votes

I have tried it a couples days ago in rails 4.

You have to change your new method into this to add property_id in photo object.

def new
  @property = Property.new
  @property.photos.new
end

Then, in your create method. I thought, you do not need to manual you photo and render to new method.

def create
  @property  = current_user.properties.new(property_params)
  if @property.save
    redirect_to property_path @property
  else
    render :new
  end
end

Next, you have to edit your field_for form into

<%= p.fields_for :photos  do |builder|  %>
  <%= builder.label :property_pic %>
  <%= builder.file_field :property_pic %>
<% end %>

Finally, change your strong parameters into

def property_params
  params.require(:property).permit(:title,:price,:numberOfBeds,:toilets,:description, photos_attributes: [:id, :property_pic])
end

I hope it can help you. This is how to make rails 4 nested form

2
votes

Further to Muhamad's answer, here's what I'd do (several changes):

#app/models/property.rb
class Property < ActiveRecord::Base
   belongs_to :user
   has_many :photos
   accepts_nested_attributes_for :photos, reject_if: :all_blank #-> seems like you're doing this incorrectly
end

#app/controllers/properties_controller.rb
class PropertiesController < ApplicationController
   def new
      @property = current_user.properties.new
      @property.photos.build
   end

   def create
      @property = current_user.properties.new property_params
      @property.save
   end

   private

   def property_params
       params.require(:property).permit(:title,:price,:number_of_beds,:toilets,:description , photos_attributes: [:property_pic])
   end
end

This should resolve your issue (your params show you're passing the nested form attributes through, although you'll need to ensure you use the fields_for helper:

#app/views/properties/new.html.erb
<%= form_for @property do |f| %>
   <%= f.fields_for :photos do |photo| %>
       <%= photo.file_field :property_pic %>
   <% end %>
   <%= f.submit %>
<% end %>

Notes

snake_case

Always, always, always call files and attributes by their snake_case name. Call classes by their CamelCase name, everything else by snake case.

-

HTML

Your HTML can also be improved big time:

#app/views/properties/new.html.erb
 <%= form_for @property do |p| %>

      <% options = [[:title, "text"], [:price, "number"], [:number_of_beds, "number"], [:toilets, "number"], [:description, "area"]] %>
 
      <% options.each do |type,input| %>
         <% input = "text_area" if input == "area" %>
         <%= p.label type %>
         <%= p.send(input, type)
      <% end %>

      <%= p.fields_for :photo  do |builder|  %>
          <%= builder.label :property_pic %>
          <%= builder.file_field :property_pic %>
      <% end %>

      <%= p.submit "Create Ad" , class:"button button-chosen"%>
  <% end %>

Also, don't use HTML for styling - only use CSS.

People use <p> and <br> to create spaces in their application, when margin-top/margin-bottom is the correct way to do it in the CSS.