Rails nested form Unpermitted parameter - ruby-on-rails

When i create or edit product, my merchant_product_detail will not be created or edit due to
Unpermitted parameter: merchant_id. But other like id and price are able to pass strong parameters, only merchant_id could not pass it. Kindly help me why my merchant_id is not permit in this case?
params return in binding.pry mode
"product"=>
{"name"=>"fewrgvers",
"description"=>"",
"product_categories_attributes"=>{"0"=>{"id"=>""}},
"merchant_product_details_attributes"=>
{"0"=>{"merchant_id"=>["2"], "id"=>"", "price"=>"123"}}
"
product_params return
Unpermitted parameter: merchant_id
=> {"name"=>"fewrgvers",
"description"=>"",
"merchant_product_details_attributes"=>{"0"=>{"id"=>"", "price"=>""}}
product.rb
has_many :merchant_product_details
accepts_nested_attributes_for :merchant_product_details, reject_if: proc { |attributes| attributes['merchant_id'].blank? }
has_many :merchants, through: :merchant_product_details
merchant.rb
has_many :merchant_product_details
has_many :products, through: :merchant_product_details
accepts_nested_attributes_for :merchant_product_details
merchant_product_detail.rb
belongs_to :product
belongs_to :merchant
product_controller.rb
def new
#product = Product.new
#product.merchant_product_details.build
end
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to root_path, notice: 'Product was successfully created.' }
else
format.html { render :new }
end
end
end
end
def update
respond_to do |format|
if #product.update_attributes(product_params)
format.html { redirect_to root_path, notice: 'Product was successfully updated.' }
else
format.html { render :edit }
end
end
end
params.require(:product).permit(:name, :description,
merchant_product_details_attributes: [:id, :merchant_id, :price]
_form.html.erb
<%= form_for(#product, :url => path, html: { class: "form-horizontal", role: "form" }) do |f| %>
<%= f.fields_for :merchant_product_details do |builder| %>
<div class="form-group row">
<%= builder.label :merchant_id, class: "col-md-2 control-label" %>
<div class="col-md-8">
<%= builder.select :merchant_id, Merchant.all.collect {|x| [x.name, x.id]}, {include_hidden: false} ,prompt: "Select something", multiple: true, class: "select2" %>
<%= builder.hidden_field :id %><br>
<%= builder.label :price %>
<%= builder.number_field :price %>
</div>
<% end %>

The problem is the multiple: true in the form field for merchant_id. This means that the param will be an array, as it could be multiple merchant ids.
If this is what you want then I would recommend changing the name to merchant_ids and allow an array like this:
params.require(:product).permit(:name, :description,
merchant_product_details_attributes: [:id, :price, merchant_ids: []])
Having a look at your model relations I think you only want to have one id though, in which case it should be enough to remove the multiple: true in the select.
<%= builder.select :merchant_id, Merchant.all.collect {|x| [x.name, x.id]}, {include_hidden: false}, prompt: "Select something", class: "select2" %>

Related

Simple_Fields_For not saving data of child record

I am currently trying to fix the relationship between delivery, delivery_orders and orders model. I've been trying all day to save a nested form that stores the orders inside of the delivery_orders table. Essentially, I want to be able to create deliveries, and then add orders to the deliveries through the delivery_order model. Normal users create orders, but Managers handle deliveries for those orders. So far, I've been unable to save the nested simple_form_for section for delivery_order model. It keep returning nil, even though the relationship is setup to support the transaction. One side note, I am unable to read the attributes of the orders through the delivery model. The code that I wrote is below. Structure User -> Order -> Manager Delivery[Delivery_order] -> Truck[Delivery]
delivery.rb
class Delivery < ApplicationRecord
belongs_to :warehouse, optional: true
belongs_to :truck, optional: true
has_one :delivery_order
has_one :order_tracker
has_many :breads
#after_create :decrement_bread_stock
accepts_nested_attributes_for :delivery_order
end
deliveryorder.rb
class DeliveryOrder < ApplicationRecord
has_many :orders, inverse_of: :delivery_order
belongs_to :delivery, optional: true
end
order.rb
class Order < ApplicationRecord
belongs_to :client, :dependent => :destroy, optional: true
belongs_to :delivery_order, optional: true
has_one :bread
has_one :order_tracker, :dependent => :destroy
after_create :build_order_tracker
geocoded_by :address, :latitude => :lat, :longitude => :lon
after_validation :geocode, if: ->(obj) {obj.client_address.present? && obj.client_address_changed?}
def address
[client_address, client_state].compact.join(', ')
end
end
deliveries_controller.rb
class DeliveriesController < ApplicationController
before_action :set_delivery, only: %i[show edit update destroy]
# GET /deliveries
# GET /deliveries.json
def index
#deliveries = Delivery.all
end
# GET /deliveries/1
# GET /deliveries/1.json
def show; end
# GET /deliveries/new
def new
#delivery = Delivery.new
#delivery.build_delivery_order
#delivery_orders = Order.all
end
# GET /deliveries/1/edit
def edit; end
# POST /deliveries
# POST /deliveries.json
def create
#delivery = current_manager.warehouse.deliveries.build(delivery_params)
respond_to do |format|
if #delivery.save
format.html { redirect_to #delivery, notice: 'Delivery was successfully created.' }
format.json { render :show, status: :created, location: #delivery }
else
format.html { render :new }
format.json { render json: #delivery.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /deliveries/1
# PATCH/PUT /deliveries/1.json
def update
respond_to do |format|
if #delivery.update(delivery_params)
format.html { redirect_to #delivery, notice: 'Delivery was successfully updated.' }
format.json { render :show, status: :ok, location: #delivery }
else
format.html { render :edit }
format.json { render json: #delivery.errors, status: :unprocessable_entity }
end
end
end
# DELETE /deliveries/1
# DELETE /deliveries/1.json
def destroy
#delivery.destroy
respond_to do |format|
format.html { redirect_to deliveries_url, notice: 'Delivery was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_delivery
#delivery = Delivery.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def delivery_params
params.require(:delivery).permit(:delivery_date, :delivery_on_time, delivery_order_attributes: %i[order_1 order_2 order_3 order_4 delivery_id order_id], orders_attributes: [:order_id])
end
end
delivery _form.html.erb
<div class="container">
<%= simple_form_for(#delivery) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<label>Estimated Delivery Date</label>
<%= f.input :delivery_date, class: 'form-control', label: false %>
</div>
<%= simple_fields_for :delivery_order, #delivery.delivery_order do |d| %>
<div class="form-control">
<label>Delivery Order 1</label>
<%= d.input :order_1, collection: #delivery_orders, :label_method => lambda {|order| "Order Id: #{order.id} | Client Name: #{order.client_name} | Client Address: #{order.client_address} | Bread Type: #{order.bread_type} | Bread Quantity: #{order.bread_quantity}" }, value_method: :id, include_blank: false, prompt: 'Add your first order to the delivery', class: 'form-control', label: false %>
</div>
<div class="form-group">
<label>Delivery Order 2</label>
<%= d.input :order_2, collection: #delivery_orders, :label_method => lambda {|order| "Order Id: #{order.id} | Client Name: #{order.client_name} | Client Address: #{order.client_address} | Bread Type: #{order.bread_type} | Bread Quantity: #{order.bread_quantity}" }, value_method: :id, include_blank: false, prompt: 'Add your second order to the delivery', class: 'form-control', label: false %>
</div>
<div class="form-group">
<label>Delivery Order 3</label>
<%= d.input :order_3, collection: #delivery_orders, :label_method => lambda {|order| "Order Id: #{order.id} | Client Name: #{order.client_name} | Client Address: #{order.client_address} | Bread Type: #{order.bread_type} | Bread Quantity: #{order.bread_quantity}" }, value_method: :id, include_blank: false, prompt: 'Add your third order to the delivery', class: 'form-control', label: false %>
</div>
<div class="form-group">
<label>Delivery Order 4</label>
<%= d.input :order_4, collection: #delivery_orders, :label_method => lambda {|order| "Order Id: #{order.id} | Client Name: #{order.client_name} | Client Address: #{order.client_address} | Bread Type: #{order.bread_type} | Bread Quantity: #{order.bread_quantity}" }, value_method: :id, include_blank: false, prompt: 'Add your fourth order to the delivery', class: 'form-control', label: false %>
</div>
<% end %>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
</div>
orders _form.html.erb
<%= simple_form_for(#order) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<label>Name</label>
<%= f.input :client_name, required: true, label: false, class: 'form-control' %>
</div>
<div class="form-group">
<label>Zip Code</label>
<%= f.input :client_zip_code, required: true, label: false, class: 'form-control' %>
</div>
<div class="form-group">
<label>Address</label>
<%= f.input :client_address, required: true, label: false, class: 'form-control' %>
</div>
<div class="form-group">
<label>State</label>
<%= f.input :client_state, required: true, label: false, class: 'form-control' %>
</div>
<div class="form-group">
<label>Choose a Bread</label>
<%= f.collection_select :bread_name, collection: Bread.all, required: true, label: false, class: 'form-control' %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
trucks_controller.rb
class TrucksController < ApplicationController
before_action :set_truck, only: %i[show edit update destroy]
before_action :authenticate_manager!
# GET /trucks
# GET /trucks.json
def index
#trucks = Truck.all
end
# GET /trucks/1
# GET /trucks/1.json
def show;
end
# GET /trucks/new
def new
#truck = Truck.new
#deliveries = Delivery.all
end
# GET /trucks/1/edit
def edit;
end
# POST /trucks
# POST /trucks.json
def create
#truck = current_manager.warehouse.trucks.build(truck_params)
respond_to do |format|
if #truck.save
format.html {redirect_to #truck, notice: 'Truck was successfully created.'}
format.json {render :show, status: :created, location: #truck}
else
format.html {render :new}
format.json {render json: #truck.errors, status: :unprocessable_entity}
end
end
end
# PATCH/PUT /trucks/1
# PATCH/PUT /trucks/1.json
def update
respond_to do |format|
if #truck.update(truck_params)
format.html {redirect_to #truck, notice: 'Truck was successfully updated.'}
format.json {render :show, status: :ok, location: #truck}
else
format.html {render :edit}
format.json {render json: #truck.errors, status: :unprocessable_entity}
end
end
end
# DELETE /trucks/1
# DELETE /trucks/1.json
def destroy
#truck.destroy
respond_to do |format|
format.html {redirect_to trucks_url, notice: 'Truck was successfully destroyed.'}
format.json {head :no_content}
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_truck
#truck = Truck.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def truck_params
params.require(:truck).permit(:delivery_total, :lon, :lat, :delivery_id, :loaded_date, :truck_driver_name, :current_street_address, :current_city, :current_state, :current_country)
end
end
truck.rb
class Truck < ApplicationRecord
belongs_to :warehouse, optional: true
has_many :deliveries
end
trucks _form.html.erb
<div class="container">
<%= simple_form_for(#truck) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<div class="col-3">
<label>Loaded Date (Click Box Below)</label>
<%= f.input :loaded_date, as: :date_time_picker, class: 'form-control', placeholder: "Tap to view calendar <i class: 'fa fa-hand-o-up'></i>", label: false %>
</div>
</div>
<div class="form-group">
<div class="col-5">
<%= f.input :delivery_total, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="col-5">
<%= f.input :truck_driver_name, class: 'form-control btn btn-outline-primary' %>
</div>
</div>
<div class="form-group">
<div class="col-5">
<%= f.input :current_street_address, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="col-5">
<%= f.input :current_city, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="col-5">
<%= f.input :current_state, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="col-5">
<%= f.input :current_country, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="col-5">
<%= f.input :delivery_id, collection: #deliveries, :label_method => lambda {|delivery| "Estimated Delivery Date: #{delivery.delivery_date} | Order Id: #{delivery.id}"}, value_method: :id, label: "Delivery Jobs", include_blank: false, prompt: 'Add a delivery to the truck' %>
</div>
</div>
<div class="form-actions">
<%= f.button :submit, class: 'btn btn-outline-success' %>
</div>
<% end %>
</div>

Nested forms not storing data into database in rails 4

I have a vendor model, a product model, and a vendor_product model. In my vendors form, I have used nested form to create vendor_products with attributes as vendor_id, product_id and copies.On creating a new vendor, it also creates a vendor_product. But for some reason, it does not stores the vendor_id and product_id in vendor_products table but only stores the copies
My associations are as follows
A vendor ->
has_many :vendor_products
has_many :products, through: :vendor_products
A product ->
has_many :vendor_products
has_many :vendors, through: :vendor_products
A vendor_product
belongs_to :vendor
belongs_to :product
Vendor.rb
class Vendor < ActiveRecord::Base
has_many :vendor_products
has_many :products, through: :vendor_products
accepts_nested_attributes_for :vendor_products, :products,
:allow_destroy => true
end
My vendors/_form.html.erb
<%= form_for(#vendor) do |f| %>
<% if #vendor.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#vendor.errors.count, "error") %> prohibited this
vendor from being saved:</h2>
<ul>
<% #vendor.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
:
:
:
<%= f.fields_for :vendor_products do |vproducts| %>
<div class="field">
<%= vproducts.label :product %><br>
<%= collection_select(:product, :product_ids, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
</div>
<div class="field">
<%= vproducts.label :copies %><br>
<%= vproducts.number_field :copies %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My vendors_controller.rb
class VendorsController < ApplicationController
before_action :set_vendor, only: [:show, :edit, :update, :destroy]
respond_to :json
def index
#vendors = Vendor.all.limit(20)
end
def show
end
def new
#vendor = Vendor.new
#vendor.products.build
#vendor.vendor_products.build
end
def edit
end
def create
#vendor = Vendor.new(vendor_params)
respond_to do |format|
if #vendor.save
format.html { redirect_to #vendor, notice: 'Vendor was successfully
created.' }
format.json { render :show, status: :created, location: #vendor }
else
format.html { render :new }
format.json { render json: #vendor.errors, status:
:unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #vendor.update(vendor_params)
format.html { redirect_to #vendor, notice: 'Vendor was successfully
updated.' }
format.json { render :show, status: :ok, location: #vendor }
else
format.html { render :edit }
format.json { render json: #vendor.errors, status:
:unprocessable_entity }
end
end
end
private
def set_vendor
#vendor = Vendor.find(params[:id])
end
def vendor_params
params.require(:vendor).permit(:name, :email, :phone_no, :addressline1,
:addressline2, :landmark,
:city, :state, :country, :pincode, :latitude, :longitude, :status,
product_attributes: [:product_id, :product_name, :price ],
vendor_products: [:vendor_product_id, :vendor_id, :product_id,
:copies])
end
end
Now a vendor and VendorProduct is created but my vendor_product looks lik this
{"id":3,
"vendor_id":null,
"product_id":null,
"copies":4,
}
Can any one point out how to fix this. What am I doing wrong. Please bear in mind that I am a rails newbie.
change once vendor_products to vendor_products_attributes and look
In your view
<%= collection_select(:product, :product_ids, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
replace with
<%= vproducts.select :product_id, options_from_collection_for_select(Product.all, "id", "name"), prompt: "Select something" %>
Change this line:
<%= collection_select(:product, :product_ids, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
</div>
to
<%= collection_select(:product, :product_id, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
</div>

Rails 4, Cocoon, ERB Template, how to make selected an options_from_collection_for_select in Edit Action?

This is my first time with Cocoon, and maybe this is a really dumb question, but I already spend many time looking how to do this with ERB template and avoid using simple_form or other helper.
Take a look of my models:
models/loot.rb
class Lot < ActiveRecord::Base
has_many :colindanciums, dependent: :destroy
has_many :cardinal_points, through: :colindanciums
accepts_nested_attributes_for :colindanciums, allow_destroy: true
end
models/colindancium.rb
class Colindancium < ActiveRecord::Base
belongs_to :cardinal_poing
belongs_to :lot
end
models/cardinal_point.rb
class CardinalPoint < ActiveRecord::Base
has_many :colindanciums
has_many :lots, through: :colindanciums
end
The form:
views/lots/_form.html.erb
<%= form_for(#lot, remote: true) do |f| %>
<%= render 'shared/error_messages', object: #lot %>
...
...
...
<fieldset id="colindancium-orientation">
<ol>
<%= f.fields_for :colindanciums do |colindancium| %>
<%= render 'colindancium_fields', f: colindancium %>
<% end %>
</ol>
<%= link_to_add_association 'Nueva Colindancia', f, :colindanciums, 'data-association-insertion-node' => "#colindancium-orientation ol", 'data-association-insertion-method' => "append" %>
</fieldset>
...
...
...
<% end %>
The partial:
views/lots/_colindancium_fields.html.erb
<li class="control-group nested-fields">
<div class="controls">
<%= f.label :description, "Descripcion:" %>
<%= f.text_field :description %>
<%= f.label :linear_meters, "Metros Lineales:" %>
<%= f.text_field :linear_meters %>
<%= f.label :cardinal_point_id, "Orientacion:" %>
<%= f.select :cardinal_point_id,
options_from_collection_for_select(CardinalPoint.all, :id, :name), { }, { :class => "form-control", :prompt => "Seleccione un Punto Cardinal" } %>
<%= link_to_remove_association "Eliminar", f %>
</div>
</li>
Everything works great when I insert new fields, it saves it in DB, it Update it in DB, my problem is in the options_from_collection_for_select when I open the form in Edit Action, the fourth parameter of this helper is the selected value... I can't find the way to make selected the value that is stored in my db, it always show the 1 index... I can't access the #... object from the _form, the other fields (:description, :linear_meters) are working quite good my problem is in the f.select, I don't know how to do it.
EDIT My controller:
# GET /lots/new
def new
#lot = Lot.new
#lot.colindanciums.build
authorize #lot
end
# PATCH/PUT /lots/1
# PATCH/PUT /lots/1.json
def update
authorize #lot
respond_to do |format|
if #lot.update(lot_params)
format.html { redirect_to #lot, notice: 'Lot was successfully updated.' }
format.json { render :show, status: :ok, location: #lot }
format.js
else
format.html { render :edit }
format.json { render json: #lot.errors, status: :unprocessable_entity }
format.js { render json: #lot.errors, status: :unprocessable_entity }
end
end
end
I change my logic in the select, i made it works in this way:
<div class="form-group">
<%= f.label :cardinal_point_id, "Orientacion:", :class => "control-label" %>
<%= f.select :cardinal_point_id , CardinalPoint.all.collect {|p| [ p.name, p.id ] }, { :include_blank => 'Seleccione un Punto Cardinal'}, :class => "form-control" %>
</div>
I post my answer in case anybody have the same issue.
You forgot to put the parenthesis correctly
<%= f.select (:cardinal_point_id,
options_from_collection_for_select(CardinalPoint.all, :id, :name), { }, { :class => "form-control", :prompt => "Seleccione un Punto Cardinal" }) %>

Can't mass-assign protected attributes: stripe_card_token

I'm trying to create a charge with stripe. I get the following error when attempting to create order object, but I have set attr_accessor :stripe_card_token. Does anyone know what I am doing wrong?
ActiveModel::MassAssignmentSecurity::Error in OrdersController#create
Can't mass-assign protected attributes: stripe_card_token
OrdersController - Create action
def create
#order = current_cart.build_order(params[:order])
#order.ip_address = request.remote_ip
#order.user_id = current_user.id
respond_to do |format|
if #order.save_with_payment
#order.add_line_items_from_cart(current_cart)
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
format.html { render :action => "success", :notice => 'Thank you for your order.' }
format.xml { render :xml => #order, :status => :created, :location => #order }
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors,
:status => :unprocessable_entity }
end
end
end
OrderModel
class Order < ActiveRecord::Base
# PAYMENT_TYPES = [ "visa", "master card", "Amex", "Discover" ] Controll the payment options via Model
attr_accessible :first_name, :last_name, :ip_address, :cart_id, :house_id
attr_accessor :stripe_card_token
belongs_to :user
belongs_to :house
belongs_to :cart
has_many :transactions, :class_name => "OrderTransaction"
has_many :line_items, :dependent => :destroy
validates :house_id, presence: true
validates :cart_id, presence: true
def price_in_cents
(cart.total_price*100).round
end
def add_line_items_from_cart(cart)
cart.line_items.each do |item|
item.cart_id = nil
line_items << item
end
end
def save_with_payment
if valid?
Stripe::Charge.create(amount: price_in_cents, currency: "cad", description: current_user.name, card: stripe_card_token)
# self.stripe_order_token = order.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
OrderView _Form
<%= f.error_notification %>
<%= f.error_messages %>
<%= f.hidden_field :stripe_card_token %>
<%= f.hidden_field :cart_id%>
<div class="form-inputs">
<p><%#if user does not have a house Make a page (please order a home valuation first) %></p>
<div class="contain">
<h3>Select House</h3>
<%= f.input :house_id, :as => :radio_buttons, :collection => current_user.houses.all.map{|h| [h.address, h.id]}%>
</div>
<%= f.input :first_name %>
<%= f.input :last_name %>
<div class="field">
<%= label_tag :card_number, "Credit Card Number" %>
<%= text_field_tag :card_number, nil, name: nil %>
</div>
<div class="field">
<%= label_tag :card_code, "Security Code on Card (CVV)" %>
<%= text_field_tag :card_code, nil, name: nil %>
</div>
<div class="field">
<%= label_tag :card_month, "Card Expiration" %>
<%= select_month nil, {add_month_numbers: true}, {name: nil, id: "card_month"} %>
<%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year+15}, {name: nil, id: "card_year"} %>
</div>
</div>
<div id="stripe_error">
<noscript>JavaScript is not enabled and is required for this form. First enable it in your web browser settings.</noscript>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
Orders.js.coffee
jQuery ->
Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'))
order.setupForm()
order =
setupForm: ->
$('#new_order').submit ->
$('input[type=submit]').attr('disabled', true)
if $('#card_number').length
order.processCard()
false
else
true
processCard: ->
card =
number: $('#card_number').val()
cvc: $('#card_code').val()
expMonth: $('#card_month').val()
expYear: $('#card_year').val()
Stripe.createToken(card, order.handleStripeResponse)
handleStripeResponse: (status, response) ->
if status == 200
$('#order_stripe_card_token').val(response.id)
$('#new_order')[0].submit()
else
$('#stripe_error').text(response.error.message)
$('input[type=submit]').attr('disabled', false)
You still need to include :stripe_card_token under attr_accessible in your model
Active record (the layer in your rails stack that provides an interface between your ruby code and your database) protects your database from unwanted end-user assignment using the attr_accessible method. if present in your model it makes sure that a request can't write to your database unless the attribute is listed.
You've got attr_accessible here but don't have :stripe_card_token listed, so you can't save to that field.
attr_accessible :first_name, :last_name, :ip_address, :cart_id, :house_id add , :stripe_card_token
You may have though the attr_accessor :stripe_card_token line would somehow be related, but that just sets the getter and setter methods for the attribute.
The difference is better laid out here
In this question
You can read more about mass-assignment here: http://www.h-online.com/security/news/item/Rails-3-2-3-makes-mass-assignment-change-1498547.html

Ruby on Rails dropdown values not saving

I'm new to RoR and having an issue when trying to save from multiple dropdowns. I have three objects - books, genres, and authors. A book object has a genre and author associated to it, but the issue is I can only manage to save either a genre or author to my book object, and not both. Here's where I'm at:
class Author < ActiveRecord::Base
validates :name, :presence => true
validates :biography, :presence => true
has_many :books
end
class Genre < ActiveRecord::Base
validates :description, :presence => true
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :genre
belongs_to :author
has_many :cartitems
validates :name, :presence => true
validates :price, :presence => true
validates :description, :presence => true
end
Controller:
def create
##book = Book.new(params[:book])
#author = Author.find(params[:author].values[0])
#genre = Genre.find(params[:genre].values[0])
#book = #author.books.create(params[:book])
#one or the other saves, but not both
##book = #genre.books.create(params[:book])
respond_to do |format|
if #book.save
format.html { redirect_to(#book, :notice => 'Book was successfully created.') }
format.xml { render :xml => #book, :status => :created, :location => #book }
else
format.html { render :action => "new" }
format.xml { render :xml => #book.errors, :status => :unprocessable_entity }
end
end
end
Not sure if this will help out or not, but here's what the dropdowns look like in the View:
<div class="field">
<%= f.label :genre %><br />
<%= #items = Genre.find(:all)
select("genre", "description", #items.map {|u| [u.description,u.id]}, {:include_blank => true})%>
Appreciate any help with this.
EDIT - Here's my full form.
<%= form_for(#book) do |f| %>
<% if #book.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#book.errors.count, "error") %> prohibited this book from being saved:</h2>
<ul>
<% #book.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :price %><br />
<%= f.text_field :price %>
</div>
<div class="field">
<%= f.label :description %><br />
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :genre %><br />
<%= #items = Genre.find(:all)
select("genre", "description", #items.map {|u| [u.description,u.id]}, {:include_blank => true})%>
</div>
<div class="field">
<%= f.label :author %><br />
<%=#items = Author.find(:all)
select("author", "name", #items.map {|u| [u.name,u.id]}, {:include_blank => true}) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Update your select fields to be defined like this:
<div class="field">
<%= f.label :genre %><br />
<%= f.select( :genre_id, Genre.all.map {|u| [u.description,u.id]}, {:include_blank => true}) %>
</div>
<div class="field">
<%= f.label :author %><br />
<%= f.select( :author_id, Author.all.map {|u| [u.name,u.id]}, {:include_blank => true}) %>
</div>
And your controller action should be like this:
def create
#book = Book.new(params[:book])
respond_to do |format|
if #book.save
format.html { redirect_to(#book, :notice => 'Book was successfully created.') }
format.xml { render :xml => #book, :status => :created, :location => #book }
else
format.html { render :action => "new" }
format.xml { render :xml => #book.errors, :status => :unprocessable_entity }
end
end
Also, remove the format.xml calls if you don't need them, they're just cluttering your controller action.
There are lots of different ways to fix your problem, it would help to see exactly what's in your params hash and what your full form looks like, but no matter. Here is one way:
#book = #author.books.create(:genre => #genre)
Here is another (essentially the same thing):
#book = #author.books.create {|book| book.genre = #genre}
You could also instantiate the book separately:
#author = Author.find(params[:author].values[0])
#genre = Genre.find(params[:genre].values[0])
#book = Book.create(:author => #author, :genre => #genre)
My guess is you didn't quite build your form correctly, otherwise your params hash would look similar to this {:book => {:author => 1, :genre => 5}} and you would be able to do this:
#book = Book.create(params[:book])
And you would not look up the author using params[:author], but would instead do params[:book][:author] if you needed to do it at all.

Resources