How to use accepts_nested_attributes_for with rails? - ruby-on-rails

I have read these documents:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
My source:
Model
app/models/product.rb
class Product < ActiveRecord::Base
has_many :product_items, foreign_key: :product_id, :dependent => :destroy
accepts_nested_attributes_for :product_items
end
app/models/product_item.rb
class ProductItem < ActiveRecord::Base
belongs_to :product
end
Controller
app/controllers/products_controller.rb
class ProductController < ApplicationController
def new
#product = Product.new
end
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to root_path }
else
format.html { render :new }
end
end
end
private
def product_params
params.require(:product).permit(:name, product_items_attributes: [ :description ])
end
end
View
app/views/products/_form.html.erb
<%= form_for(product, url: path) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :product_items do |item_form| %>
<%= item_form.label :description %>
<%= item_form.text_field :description %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
I can save the product data to the database, but it can't save product_items.
Edit
Add my post data after submit my form:
utf8:✓
authenticity_token:c5sdmoyLoN01Fxa55q6ahTQripx0GZvWU/d27C]asfdawofX9gw==
product[name]:apple
product[product_items][description]:good
commit:Submit
Edit 2
Add rails log:
Started POST "/products" for 10.0.2.2 at 2015-09-15 13:00:57 +0900
Processing by ProductController#create as HTML
I, [2015-09-15T13:00:58.039924 #28053] INFO -- : Parameters: {"utf8"=>"✓", "authenticity_token"=>"bilBzgOLc2/ZRUFhJORn+CJvCHkVJSsHQTg1V/roifoHxi9IRA==", "product"=>{"name"=>"apple", "product_items"=>{"description"=>"good"}}, "commit"=>"Submit"}

Your new method should like below in order to save product_items
def new
#product = Product.new
#product.product_items.build
end
You should also change your product_params like below for the update to work correctly in future.
def product_params
params.require(:product).permit(:name, product_items_attributes: [ :id, :description ])
end

Update your controller action product_params
def product_params
params.require(:product).permit(:name, product_items_attributes: [:id, :description, :_destroy ])
end
Either you can use nested_form gem which provide you complete demo and function control. Here is link https://github.com/ryanb/nested_form

Related

Rails: "Validation failed: Class must exist" in a Form_with

I've a multiple relation table named Order which belongs_to a Relai(to avoid singular/plurials complications), a Customer and a Composition. I set my Order model accordingly with nested_attributes as mentioned below. Before adding the customer part, I want to send a mail with just the #composition and a #relai chose with a dropdown.
class Order < ApplicationRecord
belongs_to :relai
belongs_to :composition
accepts_nested_attributes_for :relai
accepts_nested_attributes_for :composition
end
I set my OrdersController to get the :composition_id from my params
def new
#order = Order.new
#composition = Composition.find(params[:composition_id])
#relais = Relai.all
end
def create
#florist = Florist.first
#composition = Composition.find(params[:composition_id])
##relai = Relai.find(params[:relai_id]) # If I add this line it returns "Couldn't find Relai without an ID"
#order = Order.new(order_params)
if #order.save!
raise
OrderMailer.order_mail(#order).deliver
redirect_to thanks_purchase_path
else
render :new
end
end
private
def order_params
params.require(:order).permit(
florist_attributes: [:id],
relai_attributes: [:id, :name, :address],
composition_attributes: [:id, :name]
)
end
My View:
<%= form_with(model: #order, url: composition_orders_path(#composition), local: true) do |compo| %>
<%= compo.collection_select :relai, #relais, :id, :name %>
<%= compo.fields_for :composition do |fc| %>
<%= fc.collection_select :composition, #compositions, :id, :name %>
<% end %>
# Start Block that doesn't display the Relai.all
#<%#= compo.fields_for :relai do |fr| %>
#<%#= fr.label :relai, 'Ici la liste des relais :' %>
#<%#= fr.association :relai, collection: #relais %>
#<%#= fr.association :relai, #relais, :id, :name %>
#<%# end %>
# End Block
<%= compo.submit "MAIL", class: "app-form-button" %>
<% end %>
And the routes:
resources :compositions, only: [:show, :index] do
resources :orders, only: [:show, :new, :create, :index]
end
I also tried:
"nested_attributes" which seems to not works (I can't see my Relai collection in view.
=> https://www.pluralsight.com/guides/ruby-on-rails-nested-attributes)
the "optional: true" in model which throw me an error of:
PG::NotNullViolation: ERROR: null value in column "relai_id" of relation "orders" violates not-null constraint
Can someone explain me why I got a "Validation failed: Relai must exist, Composition must exist" whereas these appears in my params?
{"authenticity_token"=>"[FILTERED]", "order"=>{"relai"=>"2"}, "commit"=>"MAIL", "composition_id"=>"3"}
I'm on Rails 6.1.4 ; Ruby 2.6.6
accepts_nested_attributes_for works from parent to children. You are using it on the child side (Order).
If you just need to assign an existing Relai and Composition to Order just use a select for both of them:
class Order < ApplicationRecord
belongs_to :relai
belongs_to :composition
end
def new
#order = Order.new
#compositions = Composition.all
#relais = Relai.all
end
def create
#order = Order.new(order_params)
if #order.save!
OrderMailer.order_mail(#order).deliver
redirect_to thanks_purchase_path
else
render :new
end
end
private
def order_params
params.require(:order).permit(:relai_id, :composition_id)
end
<%= form_with(model: #order, url: composition_orders_path(#composition), local: true) do |compo| %>
<%= compo.collection_select :relai_id, #relais, :id, :name %>
<%= compo.collection_select :composition_id, #compositions, :id, :name %>
<%= compo.submit "MAIL", class: "app-form-button" %>
<% end %>
EDIT: Setting Composition on the controller.
def new
composition = Composition.find(params[:composition_id])
#order = Order.new(composition: composition)
#relais = Relai.all
end
def create
#order = Order.new(order_params)
if #order.save!
OrderMailer.order_mail(#order).deliver
redirect_to thanks_purchase_path
else
render :new
end
end
private
def order_params
params.require(:order).permit(:relai_id, :composition_id)
end
<%= form_with(model: #order, url: composition_orders_path(#composition), local: true) do |compo| %>
<%= compo.collection_select :relai_id, #relais, :id, :name %>
<%= compo.hidden_field :composition_id %>
<%= compo.submit "MAIL", class: "app-form-button" %>
<% end %>

Saving a list of emails from a form-text input into Models email_list attribute (array)

My goal is to when adding a new product with the new product form, to have an input where one can add a list of emails separated by a space. The list of emails in this string field would be saved as an array of emails in the email_list array attribute of the Product model. This way each product has many emails. (later an email will be sent to these users to fill out questionaire, once a user fills it out there name will be taken off this list and put on completed_email_list array.
I am relatively new to rails, and have a few questions regarding implementing this. I am using postgresql, which from my understanding I do not need to serialize the model for array format because of this. Below is what I have tried so far to implement this. These may show fundamental flaws in my thinking of how everything works.
My first thinking was that I can in my controllers create action first take params[:email].split and save that directly into the email_list attribute (#product.email_list = params[:email].split. It turns out that params[:email] is always nil. Why is this? (this is a basic misunderstanding I have)(I put :email as accepted param).
After spending a long time trying to figure this out, I tried the following which it seems works, but I feel this is probably not the best way to do it (in the code below), which involves creating ANOTHER attribute of string called email, and then splitting it and saving it in the email_list array :
#product.email_list = #product.email.split
What is the best way to actually implement this? someone can clear my thinking on this I would be very grateful.
Cheers
Products.new View
<%= simple_form_for #product do |f| %>
<%= f.input :title, label:"Product title" %>
<%= f.input :description %>
<%= f.input :email %>
<%= f.button :submit %>
<%end %>
Products Controller
class ProductsController < ApplicationController
before_action :find_product, only: [:show, :edit, :update, :destroy]
def index
if params[:category].blank?
#products= Product.all.order("created_at DESC")
else
#category_id=Category.find_by(name: params[:category]).id
#products= Product.where(:category_id => #category_id).order("created_at DESC")
end
end
def new
#product=current_user.products.build
#categories= Category.all.map{|c| [c.name, c.id]}
end
def show
end
def edit
#categories= Category.all.map{|c| [c.name, c.id]}
end
def update
#product.category_id = params[:category_id]
if #product.update(product_params)
redirect_to product_path(#product)
else
render 'new'
end
end
def destroy
#product.destroy
redirect_to root_path
end
def create
#product=current_user.products.build(product_params)
#product.category_id = params[:category_id]
#product.email_list = #product.email.split
if #product.save
redirect_to root_path
else
render 'new'
end
end
private
def product_params
params.require(:product).permit(:title, :description, :category_id, :video, :thumbnail,:email, :email_list)
end
def find_product
#product = Product.find(params[:id])
end
end
To solve your original issue
#product.email_list = params[:email].split. It turns out that params[:email] is always nil
:email is a sub key of :product hash, so it should be:
#product.email_list = params[:product][:email].split
Demo:
params = ActionController::Parameters.new(product: { email: "first#email.com last#email.com" })
params[:email] # => nil
params[:product][:email] # => "first#email.com last#email.com"
I'd say that what you have is perfectly fine, except for the additional dance that you're doing in #product.email_list=#product.email.split, which seems weird.
Instead, I'd have an emails param in the form and an #emails= method in the model (rather than email and #email=):
def emails=(val)
self.email_list = val.split
end
Alternatively, you could do that in the controller rather than having the above convenience #emails= method, similar to the way you're handling the category_id:
#product = current_user.products.build(product_params)
#product.category_id = params[:category_id]
#product.email_list = product_params[:emails].split
Because you need validations on your emails and to make it cleaner I would create an email table, make Product table accept Email attribues and use cocoon gem to have a nice dynamic nested form with multiple emails inputs.
1) models
class Product < ActiveRecord::Base
has_many :emails, dependent: :destroy
accepts_nested_attributes_for :emails, reject_if: :all_blank, allow_destroy: true
end
class Email < ActiveRecord::Base
belong_to :product
validates :address, presence: true
end
2) Controller
class ProductsController < ApplicationController
def new
#product = current_user.products.build
end
def create
#product = current_user.products.build(product_params)
if #product.save
redirect_to root_path
else
render 'new'
end
end
private
def product_params
params.require(:project).permit(:title, :description, :category_id, :video, :thumbnail, emails_attributes: [:id, :address, :_destroy])
end
end
3) View
<%= simple_form_for #product do |f| %>
<%= f.input :title, label:"Product title" %>
<%= f.input :description %>
<%= f.association :category %>
<div id="emails">
<%= f.simple_fields_for :emails do |email| %>
<%= render 'emails_fields', f: email %>
<div class="links">
<%= link_to_add_association 'add email', f, :emails %>
</div>
<%= end %>
</div>
<%= f.button :submit %>
<% end %>
In your _emails_fields partial:
<div class="nested-fields">
<%= f.input :address %>
<%= link_to_remove_association "Remove email", f %>
</div>
Then setup cocoon's gem and javascript and you'll be good.
Reference: https://github.com/nathanvda/cocoon

Rails - Use fields_for to create multiple nested objects

Hoping someone can help out with this. I have two models order and date_order. Each order can have multiple date_orders, and I should be able to create many date_orders as I create an order.
How do I do that? As you can see, my code is working well for creating ONE date_order and relating it to the created order.
UPDATE: I have tried to create many "builders" in my orders/new file. It worked on the view, and created an order when I entered multiple dates and times. But the fields_for did not create any date_orders.
orders_controller.rb
def new
#order = Order.new
#order.date_orders.build
end
def create
#order = Order.new(order_params)
if #order.save
flash[:success] = "blah"
redirect_to #order
else
render 'new'
end
end
private
def order_params
params.require(:order).permit(:user_id, :purpose,
date_orders_attributes: [:id, :order_date, :time_start, :time_end, :order_id])
end
order.rb
class Order < ActiveRecord::Base
has_many :date_orders, :dependent => :destroy
accepts_nested_attributes_for :date_orders, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
date_order.rb
class DateOrder < ActiveRecord::Base
belongs_to :order
end
order/new.html.erb
<%= form_for(#order, :html => {:multipart => true}) do |f| %>
## SOME QUESTIONS ##
<%= f.fields_for :date_orders do |builder| %>
<%= builder.label :date %>
<%= builder.date_field :order_date %>
<%= builder.label :starting_time %>
<%= builder.time_field :time_start %>
<%= builder.label :ending_time %>
<%= builder.time_field :time_end %>
<% end %>
<% end %>
Build more orders_dates:
class OrdersController < ApplicationController
def new
#order = Order.new
5.times { #order.date_orders.build } # < === HERE ===
end
private
def order_params
params.require(:order).permit(:user_id, :purpose,
# |- === HERE ===
date_orders_attributes: [:id, :content, :order_date, :time_start, :time_end, :order_id])
end
end
Update:
Also, add content to your strong params whitelist.

PaperClip No handler found Error

Hello I have 2 models Products and ProductImage. Product has_many ProductImages. I am using paperclip gem.
When i submit the Product Form i get this error.
No handler found for ["0", {"product_image"=>#<ActionDispatch::Http::UploadedFile:0x007f8cb78b29c8 #tempfile=#<Tempfile:/var/folders/yx/znmx6qfj0c507bvkym6lvhxh0000gn/T/RackMultipart20151221-46388-dqxfk7.jpg>, #original_filename="780069_black_l.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"product[product_images_attributes][0][product_image]\"; filename=\"780069_black_l.jpg\"\r\nContent-Type: image/jpeg\r\n">}]
def create_product_images
params["product"]["product_images_attributes"].each do |image|
ProductImage.create(product_image: image, product_id: #form.product.id)
end
end
Anyone know how to fix this? Im trying to create a ProductImage. So when i go product.product_images i have an array of all the images for the product.
Here is the form
<%= javascript_include_tag "custom" %>
<div class="container">
<div class=“row”>
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-primary">
<div class="panel-body">
<%= simple_form_for #product do |f| %>
<%= f.fields_for :product_images do |product_image| %>
<% if product_image.object.new_record? %>
<%= product_image.file_field(:product_image) %>
<% else %>
<%= image_tag(product_image.url(:thumb)) %>
<%= product_image.hidden_field :_destroy %>
<%= product_image.link_to_remove "X" %>
<% end %>
<% end %>
<%= f.collection_select :category_id, #categories, :id, :name, include_blank: true, prompt: "Select One Category" %>
<% #categories.each do |category| %>
<div class='sizes_container' id ='sizes_container_for_<%= category.id %>'>
<% category.sizes.each do |size| %>
<%= label_tag "product_form[sizes_by_id][#{size.id}]", size.title %>
<%= text_field_tag "product_form[sizes_by_id][#{size.id}]" %>
<% end %>
</div>
<% end %>
<%= f.input :title, label:"Title"%>
<%= f.input :price, label:"Price"%>
<%= f.input :description,label:"Description" %>
<%= f.input :size_description, label:"Size Details"%>
<%= f.input :shipping_description, label:"Shipping Details"%>
<%= f.input :tag_list,label:"Tags - Seperate tags using comma ','. 5 tags allowed per product" %>
<%= f.button :submit, "Create new product", class: "btn-lg btn-success" %>
<% end %>
</div>
</div>
</div>
</div>
</div>
This is what the params look like
69: def create_product_images
70: params["product"]["product_images_attributes"].each do |image|
=> 71: binding.pry
72: ProductImage.create(product_image: image, product_id: #form.product.id)
73: end
74: end
[1] pry(#<ProductsController>)> params["product"]["product_images_attributes"]
=> {"0"=>
{"product_image"=>
#<ActionDispatch::Http::UploadedFile:0x007f8cacd89b28
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"product[product_images_attributes][0][product_image]\"; filename=\"780069_black_l.jpg\"\r\nContent-Type: image/jpeg\r\n",
#original_filename="780069_black_l.jpg",
#tempfile=#<File:/var/folders/yx/znmx6qfj0c507bvkym6lvhxh0000gn/T/RackMultipart20151221-46388-d4pub9.jpg>>},
"1"=>
{"product_image"=>
#<ActionDispatch::Http::UploadedFile:0x007f8cacd897b8
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"product[product_images_attributes][1][product_image]\"; filename=\"20090a.jpg\"\r\nContent-Type: image/jpeg\r\n",
#original_filename="20090a.
Product Controller
class ProductsController < ApplicationController
before_action :set_product, only: [:edit, :show, :update]
before_action :correct_user_edit, only: [:edit, :update, :destroy]
before_action :logged_in_user, only: [:new, :edit, :update, :destroy]
def index
#products = Product.all
end
def new
#product = Product.new
#categories = Category.preload(:sizes).order(:name)
#product.product_images.build
end
def home
#products = Product.paginate(page: params[:page])
end
def edit
end
def show
end
def update
if #product.update(product_params)
redirect_to #product
flash[:success] = 'Item was successfully updated.'
else
render "edit"
end
end
def create
#form = ProductForm.new(
product_image: product_params[:product_image],
title: product_params[:title],
price: product_params[:price],
size_description: product_params[:size_description],
shipping_description: product_params[:shipping_description],
description: product_params[:description],
tag_list: product_params[:tag_list],
category_id: product_params[:category_id],
sizes_by_id: product_params[:sizes_by_id],
user: current_user
)
if #form.save
create_product_images
redirect_to #form.product
flash[:success] = "You have created a new product"
else
flash[:danger] = "Your product didn't save"
new
render "new"
end
end
def destroy
#product.destroy
flash[:success] = "Product deleted"
redirect_to user_products_path
end
private
def create_product_images
params["product"]["product_images_attributes"].each do |index, image|
ProductImage.create(product_image: image, product_id: #form.product.id)
end
end
def set_product
#product = Product.find(params[:id])
end
def product_params
params.require(:product).permit(
:product_images_attributes,
:title,
:price,
:description,
:tag_list,
:category_id,
:size_description,
:shipping_description,
).merge(sizes_by_id: params[:product_form][:sizes_by_id]) # TODO
end
def correct_user_edit
if #product = current_user.products.find_by(id: params[:id])
else
redirect_to root_url if #product.nil?
end
end
end
product_form.rb
class ProductForm
attr_reader :quantity_by_size_id, :product
def initialize(product_image:, title:, price:, description:, tag_list:, category_id:, sizes_by_id:, user:, size_description:, shipping_description:)
#quantity_by_size_id = sizes_by_id # TODO
#product_images = product_image
#product = Product.new(
title: title,
price: price,
description: description,
tag_list: tag_list,
category_id: category_id,
size_description: size_description,
shipping_description: shipping_description,
user: user
)
build_product_sizes
end
def save
product.save
end
private
def build_product_sizes
quantity_by_size_id.map do |size_id, quantity|
if quantity.to_i.nonzero?
product.product_sizes.build(product: product, size_id: size_id, quantity: quantity)
end
end
end
end
Product.rb
class Product < ActiveRecord::Base
acts_as_taggable
belongs_to :user
belongs_to :category
has_many :product_sizes
has_many :product_images, :dependent => :destroy
validates :title, presence: true, length: { maximum: 30 }
validates :description, presence: true, length: { maximum: 2000 }
validates :category, :user, :price, presence: true
accepts_nested_attributes_for :product_images, allow_destroy: true
end
schema
create_table "product_sizes", force: :cascade do |t|
t.integer "quantity"
t.integer "product_id"
t.integer "size_id"
end
product_size.rb
class ProductSize < ActiveRecord::Base
belongs_to :product
belongs_to :size
validates :quantity, presence: true
end
params
From: /Users/jo/Documents/Safsy/Website/Safsy/Safsy/app/controllers/products_controller.rb # line 36 ProductsController#create:
35: def create
=> 36: binding.pry
37: #form = ProductForm.new(
38: product_image: product_params[:product_image],
39: title: product_params[:title],
40: price: product_params[:price],
41: size_description: product_params[:size_description],
42: shipping_description: product_params[:shipping_description],
43: description: product_params[:description],
44: tag_list: product_params[:tag_list],
45: category_id: product_params[:category_id],
46: sizes_by_id: product_params[:sizes_by_id],
47: user: current_user
48: )
49: if #form.save
50: create_product_images
51: redirect_to #form.product
52: flash[:success] = "You have created a new product"
53: else
54: flash[:danger] = "Your product didn't save"
55: new
56: render "new"
57: end
58: end
[1] pry(#<ProductsController>)> params
=> {"utf8"=>"✓",
"authenticity_token"=>"3MZESoSUgrO2Bc3vl8/M0pTfTna9rjeXfXG23znsy3VGCEBcHMh+4ZC5rqSxGkQOmz4kl1JN9FFMH4lniDjFQg==",
"product"=>
{"product_images_attributes"=>
{"0"=>
{"product_image"=>
#<ActionDispatch::Http::UploadedFile:0x007f8c9ceb22f8
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"product[product_images_attributes][0][product_image]\"; filename=\"780069_black_l.jpg\"\r\nContent-Type: image/jpeg\r\n",
#original_filename="780069_black_l.jpg",
#tempfile=#<File:/var/folders/yx/znmx6qfj0c507bvkym6lvhxh0000gn/T/RackMultipart20151222-46388-pgfg4b.jpg>>,
"_destroy"=>"false"},
"1450783221367"=>
{"product_image"=>
#<ActionDispatch::Http::UploadedFile:0x007f8c9ceb2140
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"product[product_images_attributes][1450783221367][product_image]\"; filename=\"20090a.jpg\"\r\nContent-Type: image/jpeg\r\n",
#original_filename="20090a.jpg",
#tempfile=#<File:/var/folders/yx/znmx6qfj0c507bvkym6lvhxh0000gn/T/RackMultipart20151222-46388-3vamt4.jpg>>,
"_destroy"=>"false"}},
"category_id"=>"3",
"title"=>"new product",
"price"=>"352.40",
"description"=>"test",
"size_description"=>"test",
"shipping_description"=>"test",
"tag_list"=>"test"},
"product_form"=>{"sizes_by_id"=>{"1"=>"3", "2"=>"34", "3"=>""}},
"commit"=>"Create new product",
"controller"=>"products",
"action"=>"create"}
[2] pry(#<ProductsController>)> product_params
Unpermitted parameter: product_images_attributes
=> {"title"=>"new product",
"price"=>"352.40",
"description"=>"test",
"tag_list"=>"test",
"category_id"=>"3",
"size_description"=>"test",
"shipping_description"=>"test",
"sizes_by_id"=>{"1"=>"3", "2"=>"34", "3"=>""}}
[3] pry(#<ProductsController>)>
Update
After trying Rich Peck's answer i have the product images persisting and product.product_images is now working
My product_params look like this.
def product_params
params.require(:product).permit(
:title,
:price,
:description,
:tag_list,
:category_id,
:size_description,
:shipping_description,
product_images_attributes: [:product_image, :_destroy],
product_sizes_attributes: [:size_id, :quantity]
)
end
Why are you looping through your params and performing a create method?
--
You should just be able to pass the images through to your nested model with accepts_nested_attributes_for:
#app/models/product.rb
class Product < ActiveRecord::Base
has_many :product_images
accepts_nested_attributes_for :product_images, allow_destroy: true
end
#app/models/product_image.rb
class ProductImage < ActiveRecord::Base
belongs_to :product
has_attached_file :product_image
end
This should pass the nested image object from your #product form to your ProductImage model.
As long as you permit the right parameters, it should work fine for you:
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def new
#product = Product.new
#product.product_images.build
end
def create
#product = Product.new product_params
#product.save
end
private
def product_params
params.require(:product).permit(:product, :params, product_images_attributes: [:product_image] %>
end
end
Your form looks okay.
Update
Okay, in regards to saving ProductSize along with the image, you can use accetps_nested_attributes_for to save them at the same time...
I would always advocate using this instead of trying to create your own classes in an after_create call or whatever -- keeping the flow consistent is the most important thing you can do.
Anyway...
#app/models/product.rb
class Product < ActiveRecord::Base
has_many :product_images
has_many :product_sizes
accepts_nested_attributes_for :product_images, :product_sizes
end
#app/models/product_size.rb
class ProductSize < ActiveRecord::Base
belongs_to :product
end
#app/models/product_image.rb
class ProductImage < ActiveRecord::Base
belongs_to :product
has_attached_image :image
end
This will allow you to use the following controller setup:
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def new
#product = Product.new
#product.product_images.build
#product.product_sizes.build
end
def create
#product = Product.new product_params
#product.save
end
private
def product_params
params.require(:product).permit(:x, :y, :z, product_images_attributes: [:image], product_sizes_attributes: [:a, :b, :c])
end
end
This will allow you to use a similar setup to what you have currently:
#app/views/products/new.html.erb
<%= form_for #product do |f| %>
<%= f.fields_for :product_images do |p| %>
<%= p.file_field :image %>
<% end %>
<%= f.fields_for :product_sizes do |s| %>
<%= s.text_field :a %>
<% end %>
<%= f.submit %>
<% end %>
This will allow you to use all the validations you require etc, and if you use Cocoon, you'll be able to add extra sizes as you require.
You don't have an Array, it seems a Hash there.
Your Hash looks like {"ID" => IMAGE}
Try to iterate over an id and image like this:
params["product"]["product_images_attributes"].each do |index, image|

Rails: Form inputs not saving after submit

why wont anything entered into this form save?
new.html.erb
<%= form_for [#requestable, #request] do |f| %>
<%= f.label :status %>
<%= f.text_field :status, rows: 8 %>
<%= f.submit "Request", :class => 'btn'%>
<% end %>
requests_controller.rb
class RequestsController < ApplicationController
before_filter :load_requestable
def index
#requests = #requestable.requests
end
def new
#request = #requestable.requests.new
end
def create
#request = #requestable.requests.new(params[:status])
if #request.save
redirect_to [#requestable, :requests], notice: "Request sent."
else
render :new
end
end
private
def load_requestable
klass = [Company, Profile].detect { |c| params["#{c.name.underscore}_id"]}
#requestable = klass.find(params["#{klass.name.underscore}_id"])
end
end
my controller is based on this
https://github.com/railscasts/154-polymorphic-association-revised/blob/master/blog-after/app/controllers/comments_controller.rb
request.rb
class Request < ActiveRecord::Base
attr_accessible :status
belongs_to :requestable , polymorphic: true
belongs_to :profile
validates :status, presence: true
end
This is being produced by my debuger
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
utf8: ✓
authenticity_token: /0H2k89HN4JVXBPsoFWen5rUfx2xr4p5hr1uDSQVlcA=
request: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
status: pending
commit: Request
action: create
controller: requests
company_id: '1'
Take a look at what's in your params hash. The status field is probably in something like params[:request][:status]. Assuming standard activerecord-y stuff, you want to pass the hash for the whole request object to .new.

Resources