I don't understand why I'm geting these Unpermitted parameter: format messages, I'm doing a JSON request: POST "/questions/add_options.json" with these parameters Parameters: {"id_question"=>551, "options"=>[{"position"=>10, "label"=>"opc 10", "value"=>"opc 10", "go_page"=>nil}], "question"=>{}} and this is what I get in the terminal...
Started POST "/questions/add_options.json" for 127.0.0.1 at 2016-08-16 23:12:27 -0300
Processing by QuestionsController#add_options as JSON
Parameters: {"id_question"=>551, "options"=>[{"position"=>10, "label"=>"opc 10", "value"=>"opc 10", "go_page"=>nil}], "question"=>{}}
User Load (0.4ms) SELECT "login_aexa".* FROM "login_aexa" WHERE "login_aexa"."usuaex_id" = $1 ORDER BY "login_aexa"."usuaex_id" ASC LIMIT 1 [["usuaex_id", 1]]
Unpermitted parameter: format
Question Load (0.4ms) SELECT "questions".* FROM "questions" WHERE "questions"."id" = $1 LIMIT 1 [["id", 551]]
Unpermitted parameter: format
(0.2ms) BEGIN
(0.4ms) SELECT COUNT(*) FROM "options" WHERE "options"."question_id" = $1 [["question_id", 551]]
In the Rails controller I use params permit to reject parameters that are not allowed, like this:
def question_add_options_params
params.permit(:id_question, options: [:position, :label, :value, :go_page], question: {})
end
In my opinion the format should be fine, anyone know why I'm getting those Unpermitted parameter: format messages?
EDIT:
Here's the code of the controller
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /questions
# GET /questions.json
def index
#questions = Question.all
end
# GET /questions/1
# GET /questions/1.json
def show
end
# GET /questions/new
def new
#question = Question.new
end
# GET /questions/1/edit
def edit
end
# POST /questions
# POST /questions.json
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1
# PATCH/PUT /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
format.html { redirect_to #question, notice: 'Question was successfully updated.' }
format.json { render :show, status: :ok, location: #question }
else
format.html { render :edit }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
def add_options
#question = Question.find(question_add_options_params[:id_question])
question_add_options_params[:options].each do|q_aop|
#question.options.create(q_aop)
end
#options = #question.options
end
# DELETE /questions/1
# DELETE /questions/1.json
def destroy
#question.destroy
respond_to do |format|
format.html { redirect_to questions_url, notice: 'Question was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params[:question]
end
def question_add_options_params
params.permit(:id_question, options: [:position, :label, :value, :go_page])
end
end
params.permit(:id_question, options: [:position, :label, :value, :go_page], question: {})
This line is telling Rails that the only params that are permitted are in the list above.
If you actually look at a real params-hash, it doesn't just contain the params passed in by the form, it also contains things like: :controller => :questions, :action => :create, :format => :json etc... which Rails always inserts based on the URL
Normally we namespace the form by using eg form_for #question which means the params come in like this:
{:controller => :questions, :action => :create,
:format => :json,
:question => {"id_question"=>551, "options"=>[{"position"=>10, "label"=>"opc 10", "value"=>"opc 10", "go_page"=>nil}]}
}
then you can do this in your controller:
params.require(:question).permit(:id_question, options: [:position, :label, :value, :go_page])
which doesn't literally tell rails that you aren't allowed to have the controller/action/format params that are always passed in by rails...
Obviously you'll need to modify the names of these to suit your needs, but this is what you need to do to stop the error.
I was having a similar issue with my JSON and thought this question could use a few more examples.
TL;DR
To avoid the Unpermitted parameter: format with JSON requests be sure to nest your request object for POST and PATCH requests (ex. { question: {name: '', prompt: ''} } and access with params.require(:question).permit(:name, :prompt, ..) in the controller. For GET and DELETE requests, use only params.require(:id) in the controller.
To build on Taryn's reply above, I needed to create a nested object in my front end for POST requests, and fix how I was using require and permit on the back end. Here are examples:
Example: POST /questions
Rails expects a post request to be of the form, { question: { name: '', prompt: '', ..} }. In the front end:
// bad
$http.post('/questions.json', { name: 'Question 1', prompt: 'Bears eat beets?' })
// good
$http.post('/questions.json', { question: { name: 'Question 1', prompt: '...' } })
Backend:
# app/controllers/questions_controller.rb
def question_params
# bad - logs 'Unpermitted parameter: format'
params.permit(:name, :prompt)
# good
params.require(:question).permit(:name, :prompt)
end
Example: GET /questions/:id
My mistake was in the backend again. For example:
# app/controllers/questions_controller.rb
def show
# bad - logs 'Unpermitted parameter: format'
question = Question.where(params.permit(:id)).first
# good
question = Question.find(params.require(:id))
render json: question
end
<%= link_to "Questinons", questions_path(format: "json") %>
Related
my rails version is 5.0.5, i am currently developing an online store. i am following the steps on Agile web development (rails 5). i have followed the steps accordingly, i can create new products, edit products,and delete products, i am stuck at the stage where i am to add a product to cart(the "Add to cart" button shows) when i click "Add to cart" it gives me an error{cant find product with id}..........this is the error guys.
ruby 2.4.1p111 (2017-03-22 revision 58053) [i386-mingw32]
C:\Users\COMPUTER>cd desktop
C:\Users\COMPUTER\Desktop>cd trial
C:\Users\COMPUTER\Desktop\trial>cd depot
C:\Users\COMPUTER\Desktop\trial\depot>rails s
=> Booting Puma
=> Rails 5.0.5 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
*** SIGUSR2 not implemented, signal based restart unavailable!
*** SIGUSR1 not implemented, signal based restart unavailable!
*** SIGHUP not implemented, signal based logs reopening unavailable!
Puma starting in single mode...
* Version 3.10.0 (ruby 2.4.1-p111), codename: Russell's Teapot
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
Started GET "/" for 127.0.0.1 at 2017-10-22 17:44:40 +0100
ActiveRecord::SchemaMigration Load (0.0ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by StoreController#index as HTML
Rendering store/index.html.erb within layouts/application
Product Load (4.0ms) SELECT "products".* FROM "products" ORDER BY "products"."title" ASC
Rendered store/index.html.erb within layouts/application (620.0ms)
Completed 200 OK in 1888ms (Views: 1385.1ms | ActiveRecord: 20.0ms)
Started POST "/line_items" for 127.0.0.1 at 2017-10-22 17:46:14 +0100
Processing by LineItemsController#create as HTML
Parameters: {"authenticity_token"=>"tysgjA3w3dfMA1ACBRWeigDDnDM9WDiwBDKotUwmXVVxVy0+msnR4gmkB3QV8qU8dKdLdnb5yEOS5FmdUs7LyQ=="}
Cart Load (4.0ms) SELECT "carts".* FROM "carts" WHERE "carts"."id" = ? LIMIT ? [["id", nil], ["LIMIT", 1]]
(4.0ms) begin transaction
SQL (48.0ms) INSERT INTO "carts" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2017-10-22 16:46:15.531901"], ["updated_at", "2017-10-22 16:46:15.531901"]]
(108.0ms) commit transaction
Product Load (4.0ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ? [["id", nil], ["LIMIT", 1]]
Completed 404 Not Found in 644ms (ActiveRecord: 180.0ms)
ActiveRecord::RecordNotFound (Couldn't find Product with 'id'=):
app/controllers/line_items_controller.rb:29:in `create'
Rendering C:/Ruby24/lib/ruby/gems/2.4.0/gems/actionpack-5.0.5/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout
Rendering C:/Ruby24/lib/ruby/gems/2.4.0/gems/actionpack-5.0.5/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
Rendered C:/Ruby24/lib/ruby/gems/2.4.0/gems/actionpack-5.0.5/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (52.0ms)
Rendering C:/Ruby24/lib/ruby/gems/2.4.0/gems/actionpack-5.0.5/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
Rendered C:/Ruby24/lib/ruby/gems/2.4.0/gems/actionpack-5.0.5/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (24.0ms)
Rendering C:/Ruby24/lib/ruby/gems/2.4.0/gems/actionpack-5.0.5/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
Rendered C:/Ruby24/lib/ruby/gems/2.4.0/gems/actionpack-5.0.5/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (16.0ms)
Rendered C:/Ruby24/lib/ruby/gems/2.4.0/gems/actionpack-5.0.5/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (8928.0ms)
also below is the store(the default homepage that shows the products) view file.....
<p id="notice"><%= notice %></p>
<h1>Your Pragmatic Catalog</h1>
<% cache #products do %>
<% #products.each do |product| %>
<% cache #product do %>
<div class="entry">
<h3><%= product.title %></h3>
<%= image_tag(product.image_url) %>
<%= sanitize(product.description) %>
<div class="price_line">
<span class="price"><%= number_to_currency (product.price) %></span>
<%= button_to 'Add to Cart' , line_items_path %>
</div>
</div>
<% end %>
<% end %>
<% end %>
als below is the routes......
Rails.application.routes.draw do
resources :line_items
resources :carts
root 'store#index', as: 'store_index'
resources :products
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
and below is the products model ......
class Product < ApplicationRecord
validates :title, :description, :image_url, presence: true
validates :price, numericality: {greater_than_or_equal_to: 0.01}
validates :title, uniqueness: true
validates :image_url, allow_blank: true, format: {
with: %r{\.(gif|jpg|png)\Z}i,
message: 'must be a URL for GIF, JPG or PNG image.'
}
has_many :line_items
before_destroy :ensure_not_referenced_by_any_line_item
#...
private
# ensure that there are no line items referencing this product
def ensure_not_referenced_by_any_line_item
unless line_items.empty?
errors.add(:base, 'Line Items present')
throw :abort
end
end
end
below is the carts model....
class Cart < ApplicationRecord
has_many :line_items, dependent: :destroy
end
def add_product (lineitem, product_id)
current_item = line_items.find_by(product_id: product.id)
if current_item
current_item.quantity += 1
else
current_item = line_items.build(product_id: product.id)
end
current_item
end
line items model....
class LineItem < ApplicationRecord
belongs_to :product
belongs_to :cart
end
application_record.rb........
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
carts controller....
class CartsController < ApplicationController
before_action :set_cart, only: [:show, :edit, :update, :destroy]
# GET /carts
# GET /carts.json
def index
#carts = Cart.all
end
# GET /carts/1
# GET /carts/1.json
def show
end
# GET /carts/new
def new
#cart = Cart.new
end
# GET /carts/1/edit
def edit
end
# POST /carts
# POST /carts.json
def create
#cart = Cart.new(cart_params)
respond_to do |format|
if #cart.save
format.html { redirect_to #cart, notice: 'Cart was successfully created.' }
format.json { render :show, status: :created, location: #cart }
else
format.html { render :new }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /carts/1
# PATCH/PUT /carts/1.json
def update
respond_to do |format|
if #cart.update(cart_params)
format.html { redirect_to #cart, notice: 'Cart was successfully updated.' }
format.json { render :show, status: :ok, location: #cart }
else
format.html { render :edit }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
# DELETE /carts/1
# DELETE /carts/1.json
def destroy
#cart.destroy
respond_to do |format|
format.html { redirect_to carts_url, notice: 'Cart was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_cart
#cart = Cart.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def cart_params
params.fetch(:cart, {})
end
end
application controller.....
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
end
line items controller....
class LineItemsController < ApplicationController
include CurrentCart
before_action :set_cart, only: [:create]
before_action :set_line_item, only: [:show, :edit, :update, :destroy]
# GET /line_items
# GET /line_items.json
def index
#line_items = LineItem.all
end
# GET /line_items/1
# GET /line_items/1.json
def show
end
# GET /line_items/new
def new
#line_item = LineItem.new
end
# GET /line_items/1/edit
def edit
end
# POST /line_items
# POST /line_items.json
def create
product = Product.find (params[:product_id])
#line_item = #cart.line_items.build(product: product)
respond_to do |format|
if #line_item.save
format.html { redirect_to #line_item.cart, notice: 'Line item was successfully created.' }
format.json { render :show, status: :created, location: #line_item }
else
format.html { render :new }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /line_items/1
# PATCH/PUT /line_items/1.json
def update
respond_to do |format|
if #line_item.update(line_item_params)
format.html { redirect_to #line_item, notice: 'Line item was successfully updated.' }
format.json { render :show, status: :ok, location: #line_item }
else
format.html { render :edit }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /line_items/1
# DELETE /line_items/1.json
def destroy
#line_item.destroy
respond_to do |format|
format.html { redirect_to line_items_url, notice: 'Line item was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_line_item
#line_item = LineItem.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def line_item_params
params.require(:line_item).permit(:product_id, :cart_id)
end
end
products controller...
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:title, :description, :image_url, :price)
end
end
store controller.....
class StoreController < ApplicationController
def index
#products = Product.order(:title)
end
end
Your error is ActiveRecord::RecordNotFound (Couldn't find Product with 'id'=):. This means that the controller doesn't know which product you want to add to the cart. This would be done by adding a product_id to the params of your POST.
In your view:
<%= button_to 'Add to Cart' , line_items_path %>
The line_items_path probably isn't correct. You probably want line_item_path(product_id: product.id). This will add the product's ID to the request and make it available when the controller tries to find the record here:
product = Product.find (params[:product_id])
In the view you have to send the correct product_id as suggested by Daniel Westendorf
<%= button_to 'Add to Cart' , line_items_path(product_id: product.id) %>
So, in this way when you create a new LineItem in new method of LinesItemController, you have it params
def new
#line_item = LineItem.new(params[:product_id])
end
For using the debug mode you can use byebug and you can use this step_by_step installation guide.
pry is another alternative.
I am a rails newbie and building a little application to help with my work.
I have client, site and quote models and controllers with views set up.
I have created a form on the quote model that pulls data from the other two models in a collection_select field. The documentation on collection_select for rails that I have found is pretty bad. I want to take a client name and site name and associate/ display the name on the quote.
I have set this up in the form, but it does not save the data or show it.
I really want to understand the inputs for the collection_select as I am sure mine are probably wrong and causing the issue.
<%= f.collection_select :client, Client.all, :quote_client, :client_name , {:prompt => "Please select a client for the site"} %>
I did some research and learned this from #juanpastas here
My form looks like so
quotes/views/_form.html
<%= form_for(quote) do |f| %>
<% if quote.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(quote.errors.count, "error") %> prohibited this quote from being saved:</h2><ul>
<% quote.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %><div class="field">
<%= f.label :client %>
<%= f.collection_select :client, Client.all, :quote_client, :client_name , {:prompt => "Please select a client for the site"} %>
</div><div class="field">
<%= f.label :site_name %>
<%= f.collection_select :site, Site.all, :quote_site, :site_name , {:prompt => "Please select a site for the quote"} %>
</div><div class="field">
<%= f.label :quote_contact %>
<%= f.text_field :quote_contact %>
</div><div class="field">
<%= f.label :quote_value %>
<%= f.text_field :quote_value %>
</div><div class="field">
<%= f.label :quote_description %>
<%= f.text_field :quote_description %>
</div><div class="actions">
<%= f.submit %>
</div>
<% end %>
EDIT
Answers/clarifications
Quotes can only have one client and one site. The site would also have to belong to the client.
I have a list of clients called from the Client model via Client.all and a list of sites via the Site Model called via Site.all. I only need the name of one Client and one Site for each quote but want to be able to select in a cascading fashion. Select Client, then selects Site from those available for the Client.
Relations are set up between the three models like so:
class Quote < ApplicationRecord
belongs_to :site, optional: true
belongs_to :client, optional: true
has_and_belongs_to_many :assets
end
class Site < ApplicationRecord
has_attached_file :site_image, styles: { small: "64x64", med: "100x100", large: "200x200" }
do_not_validate_attachment_file_type :site_image
belongs_to :client , optional: true
has_and_belongs_to_many :assets
has_and_belongs_to_many :quotes
end
class Client < ApplicationRecord
has_and_belongs_to_many :sites
has_and_belongs_to_many :assets
has_and_belongs_to_many :quotes
end
Controllers
class QuotesController < ApplicationController
before_action :set_quote, only: [:show, :edit, :update, :destroy]
# GET /quotes
# GET /quotes.json
def index
#quotes = Quote.all
end
# GET /quotes/1
# GET /quotes/1.json
def show
end
# GET /quotes/new
def new
#quote = Quote.new
end
# GET /quotes/1/edit
def edit
end
# POST /quotes
# POST /quotes.json
def create
#quote = Quote.new(quote_params)
respond_to do |format|
if #quote.save
format.html { redirect_to #quote, notice: 'Quote was successfully created.' }
format.json { render :show, status: :created, location: #quote }
else
format.html { render :new }
format.json { render json: #quote.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /quotes/1
# PATCH/PUT /quotes/1.json
def update
respond_to do |format|
if #quote.update(quote_params)
format.html { redirect_to #quote, notice: 'Quote was successfully updated.' }
format.json { render :show, status: :ok, location: #quote }
else
format.html { render :edit }
format.json { render json: #quote.errors, status: :unprocessable_entity }
end
end
end
# DELETE /quotes/1
# DELETE /quotes/1.json
def destroy
#quote.destroy
respond_to do |format|
format.html { redirect_to quotes_url, notice: 'Quote was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_quote
#quote = Quote.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def quote_params
params.require(:quote).permit(:quote_client, :quote_site, :client_name, :site_name, :quote_contact, :quote_value, :quote_description)
end
end
class SitesController < ApplicationController
before_action :set_site, only: [:show, :edit, :update, :destroy]
# GET /sites
# GET /sites.json
def index
#sites = Site.all
#clients = Client.all
end
# GET /sites/1
# GET /sites/1.json
def show
#sites = Site.all
#clients = Client.all
end
# GET /sites/new
def new
#site = Site.new
end
# GET /sites/1/edit
def edit
end
# POST /sites
# POST /sites.json
def create
#site = Site.new(site_params)
respond_to do |format|
if #site.save
format.html { redirect_to #site, notice: 'Site was successfully created.' }
format.json { render :show, status: :created, location: #site }
else
format.html { render :new }
format.json { render json: #site.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /sites/1
# PATCH/PUT /sites/1.json
def update
respond_to do |format|
if #site.update(site_params)
format.html { redirect_to #site, notice: 'Site was successfully updated.' }
format.json { render :show, status: :ok, location: #site }
else
format.html { render :edit }
format.json { render json: #site.errors, status: :unprocessable_entity }
end
end
end
# DELETE /sites/1
# DELETE /sites/1.json
def destroy
#site.destroy
respond_to do |format|
format.html { redirect_to sites_url, notice: 'Site was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_site
#site = Site.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def site_params
params.require(:site).permit(:site_client, :client_name, :site_name, :site_image, :site_address, :site_contact)
end
end
class ClientsController < ApplicationController
before_action :set_client, only: [:show, :edit, :update, :destroy]
# GET /clients
# GET /clients.json
def index
#clients = Client.all
#sites = Site.all
end
# GET /clients/1
# GET /clients/1.json
def show
#clients = Client.all
#sites = Site.all
end
# GET /clients/new
def new
#client = Client.new
end
# GET /clients/1/edit
def edit
end
# POST /clients
# POST /clients.json
def create
#client = Client.new(client_params)
respond_to do |format|
if #client.save
format.html { redirect_to #client, notice: 'Client was successfully created.' }
format.json { render :show, status: :created, location: #client }
else
format.html { render :new }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /clients/1
# PATCH/PUT /clients/1.json
def update
respond_to do |format|
if #client.update(client_params)
format.html { redirect_to #client, notice: 'Client was successfully updated.' }
format.json { render :show, status: :ok, location: #client }
else
format.html { render :edit }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# DELETE /clients/1
# DELETE /clients/1.json
def destroy
#client.destroy
respond_to do |format|
format.html { redirect_to clients_url, notice: 'Client was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_client
#client = Client.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def client_params
params.require(:client).permit(:client_name, :client_address, :client_phone, :client_email, :client_website)
end
end
Additions
You may notice I have tried to scale so that a client is called in a site and a site and a client is called in a quote.
First of all: I'm assuming you have relations set up between the three models! There has to be a has_many relation from quote to client and from quote to site.
There are two issues which could prevent your form from saving.
Firstly it is in how you create your collection_select. The third parameter in collection select is what will be sent to the controller. This should be an array of IDs (I assume a quote can have more than ONE client). I see you call it :quote_client. I'd rename it to :client_ids. In the end that's what you want to send to your controller: an array of IDs.
The second thing you have to take care of is your controller. It would be nice if you shared your controller code, but I assume you have a quotes_controller with a quote_params method inside it. It will probably look like this:
def quote_params
params.require(:quote).permit(:quote_contact, etc., etc.)
end
This controller method has to respond with your form_for, so every field in your form_for (like quote_contact) should be in the permit, otherwise it won't get saved. If you want to save an array of IDs, you have to tell this method you're expecting an array of IDs. You can do that like so: client_ids: [].
So your new quote_params method should look like this:
def quote_params
params.require(:quote).permit(:quote_contact, client_ids: [], site_ids: [], all_other_fields...)
end
I hope this answer provides you with your much needed help. If I need to clarify more: just ask :)
Cheers
EDIT: the answer above is still relevant for those who do want to save multiple records, but because you stated you do only want to save one record here is my updated answer:
The logic I summed up above stays roughly the same.
What you do not seem to understand at the moment, and what is (IMO) quite vital to understanding Rails applications is the way forms map to controllers and controllers map to the database. The method quote_params, as stated above, should permit all fields from forms you want to save to the database. This means all fields in your permit-part should BE in your database, otherwise they can't be saved. If you look closely at your quote table in the database, you will see that it has fields for client_id and site_id. These two fields hold the reference for your quote/client and quote/site associations. That is why your permit currently is not working, because you have quote_client and quote_site in place. The database does not have a quote_client or quote_site and hence when trying to save, doesn't update associations. The database does have client_id and site_id, so that's what you should pass into your quote params method.
This should of course correspond to the fields in your form_for. So you need change two things to make this work:
Change your two collection_selects and swap :quote_client for :client_id and :quote_site for :site_id.
Change your controller method to reflect the changes in your form_for. Here also you have to swap quote_site and quote_client for quote_id and site_id, like this:
def quote_params
params.require(:quote).permit(:client_id, :site_id, etc.)
end
The important thing to remember when using Rails MODELNAME_params methods (which we call strong parameters -> READ IT! http://edgeguides.rubyonrails.org/action_controller_overview.html)
is that both your form and your permit action should list the fields EXACTLY like they are in the database, otherwise the database won't understand and your record won't be properly saved.
I hope with this edit you'll figure it out.
Cheers
I'm having trouble getting Best In Place to update. This is the first time I've used this gem or made a Rails app so I'm sure I'm just missing something simple even though I've scoured the documentation and searched through other questions here.
This is the error I get in the console:
Started PUT "/tasks/1" for 127.0.0.1 at 2016-04-22 11:52:10 -0600
Processing by TasksController#update as JSON
Parameters: {"authenticity_token"=>"cAwRbx8JeYDMG1LrR7nl0VQfwW/x00RUd8rsTP8Iwc
0=", "tasks"=>{"title"=>"Task 1sadsads"}, "id"=>"1"}
Task Load (0.0ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = ? ORD
ER BY priority ASC LIMIT 1 [["id", 1]]
CACHE (0.0ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = ? ORDER B
Y priority ASC LIMIT 1 [["id", "1"]]
Completed 400 Bad Request in 3ms
ActionController::ParameterMissing (param is missing or the value is empty: task
):
app/controllers/tasks_controller.rb:99:in `task_params'
app/controllers/tasks_controller.rb:62:in `update'
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8
/lib/action_dispatch/middleware/templates/rescues/_source.erb (1.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8
/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb (0.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8
/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb
(0.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8
/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb (36.0ms)
My controller:
class TasksController < ApplicationController
before_action :set_task, only: [:show, :edit, :update, :destroy]
# GET /tasks
# GET /tasks.json
def index
#tasks = Task.all
respond_to do |type|
type.html
type.json {render :json => #task}
end
end
# GET /tasks/1
# GET /tasks/1.json
def show
#task = Task.find params[:id]
respond_to do |format|
format.html
format.json {render :json => #task}
end
end
# GET /tasks/new
def new
#task = Task.new
end
# GET /tasks/1/edit
def edit
end
# POST /tasks
# POST /tasks.json
def create
#task = Task.new(task_params)
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render :show, status: :created, location: #task }
else
format.html { render :new }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
def sort
params[:order].each do |key,value|
Task.find(value[:id]).update_attribute(:priority,value[:position])
end
render :nothing => true
end
# PATCH/PUT /tasks/1
# PATCH/PUT /tasks/1.json
def update
#task = Task.find params[:id]
if #task.update(task_params)
respond_to do |format|
format.html { redirect_to( #task )}
format.json { render :json => #task }
end
else
respond_to do |format|
format.html { render :action => :edit } # edit.html.erb
format.json { render :nothing => true }
end
end
end
# DELETE /tasks/1
# DELETE /tasks/1.json
def destroy
#task.destroy
respond_to do |format|
format.html { redirect_to tasks_url, notice: 'Task was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_task
#task = Task.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def task_params
params.require(:task).permit(:title, :description, :priority)
end
end
My view partial:
<div class="panel panel-default" data-id="<%= task.id %>">
<div class="panel-heading">
<h3 class="panel-title"><span class="rest-in-place" data-url="/tasks/<%= task.id %>" data-object="tasks" data-attribute="title" data-placeholder="Enter a title">
<%= task.title %>
</span></h3>
</div>
<div class="panel-body">
<%= truncate task.description, length: 50 %>
</div>
</div>
I really appreciate any help I can get. I'm 99% sure it's something I configured wrong in the controller, I just can't for the life of me figure out what it is.
EDIT: The background for this is that I have a bunch of 'Tasks' and I want to list them all on the index and be able to update any task on the index just by clicking on the title.
ActionController::ParameterMissing (param is missing or the value is empty: task
):
app/controllers/tasks_controller.rb:99:in `task_params'
app/controllers/tasks_controller.rb:62:in `update'
This is telling you that there is no parameter with the name of :task. You can see your parameters above this message.
Parameters: {"authenticity_token"=>"cAwRbx8JeYDMG1LrR7nl0VQfwW/x00RUd8rsTP8Iwc
0=", "tasks"=>{"title"=>"Task 1sadsads"}, "id"=>"1"}
You have tasks in your params here. This information is coming from your view.
data-object="tasks"
Try changing this piece in your view to task and it should send the request correctly.
Following codeschool.com's ruby screencast on making an app and ran into this error.
Full error is
ActiveRecord::RecordNotFound in DestinationsController#show
Couldn't find Trip with 'id'=
The error applies to the #trip instance below
GET /destinations/1.json
def show
#trip = Trip.find(params[:trip_id])
Here is the applicable code from the destinations_controller.rb:
def show
#trip = Trip.find(params[:trip_id])
#destination = Destination.find(params[:id])
end
Here is the Trip model
class Trip < ActiveRecord::Base
has_many :destinations
end
And the Destination model
class Destination < ActiveRecord::Base
belongs_to :trip
end
Routes
Rails.application.routes.draw do
resources :destinations
resources :trips do
resources :destinations
end
root to: 'trips#index'
Any help is greatly appreciated. :) :) :)
Update 1: From log files
Started GET "/destinations/4" for ::1 at 2016-03-31 00:50:08 +0900
Processing by DestinationsController#show as HTML Parameters:
{"id"=>"4"}
[1m[35mDestination Load (0.6ms)[0m SELECT "destinations".* FROM
"destinations" WHERE "destinations"."id" = ? LIMIT 1 [["id", 4]]
[1m[36mTrip Load (0.3ms)[0m [1mSELECT "trips".* FROM "trips"
WHERE "trips"."id" = ? LIMIT 1[0m [["id", nil]]
Completed 404 Not Found in 20ms (ActiveRecord: 1.8ms)
ActiveRecord::RecordNotFound (Couldn't find Trip with 'id'=):
app/controllers/destinations_controller.rb:14:in `show'*
Update 2 : the destinations_controller in its entirety.
class DestinationsController < ApplicationController
before_action :set_destination, only: [:show, :edit, :update, :destroy]
# GET /destinations
# GET /destinations.json
def index
#destinations = Destination.all
end
# GET /destinations/1
# GET /destinations/1.json
def show
Rails.logger.debug params.inspect
#trip = Trip.find(params[:trip_id])
#destination = Destination.find(params[:id])
end
# GET /destinations/new
def new
#trip = Trip.find(params[:trip_id])
#destination = Destination.new
end
# GET /destinations/1/edit
def edit
#trip = Trip.find(params[:trip_id])
#destination = Destination.find(set_destination)
end
# POST /destinations
# POST /destinations.json
def create
#trip = Trip.find(params[:trip_id])
#destination = #trip.destinations.new(destination_params)
respond_to do |format|
if #destination.save
format.html { redirect_to trip_destination_path(#trip, #destination), notice: 'Destination was successfully created.' }
format.json { render :show, status: :created, location: #destination }
else
format.html { render :new }
format.json { render json: #destination.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /destinations/1
# PATCH/PUT /destinations/1.json
def update
respond_to do |format|
if #destination.update(destination_params)
format.html { redirect_to #destination, notice: 'Destination was successfully updated.' }
format.json { render :show, status: :ok, location: #destination }
else
format.html { render :edit }
format.json { render json: #destination.errors, status: :unprocessable_entity }
end
end
end
# DELETE /destinations/1
# DELETE /destinations/1.json
def destroy
#destination.destroy
respond_to do |format|
format.html { redirect_to destinations_url, notice: 'Destination was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_destination
#destination = Destination.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def destination_params
params.require(:destination).permit(:name, :description)
end
end
Change the show action to this:
def show
#trip = #destination.trip
end
Edit: Removed #destination assignment here because of the before_action running set_destination.
The Destination model has one Trip:
class Destination < ActiveRecord::Base
belongs_to :trip
end
Since you're setting the #destination because id is actually passed over, you can just get #trip through association.
In your routes you currently have a nested route for destinations:
resources :trips do
resources :destinations
end
This means a destination is expected to be accessed in the context of its trip.
e.g. GET /trips/1/destinations/1.json where you'll have a trip_id parameter for the trip and an id parameter for the id of the destination.
You're also defining an non-nested route for destinations:
resources :destinations
but your DestinationController's show action assumes the nested version is being used when it does:
#trip = Trip.find(params[:trip_id])
#destination = Destination.find(params[:id])
Have a check that the GET request matches what's being shown in the screencast - or post a link to the exact screencast you're following.
I have two models post and topic in my rails app
class Post < ActiveRecord::Base
#relation between topics and post
belongs_to :topic
#post is valid only if it's associated with a topic:
validates :topic_id, :presence => true
#can also require that the referenced topic itself be valid
#in order for the post to be valid:
validates_associated :topic
end
And
class Topic < ActiveRecord::Base
#relation between topics and post
has_many :posts
end
I am trying to create association between both of them.
I want multiple post corresponding to each topic
I have used nested routes
Rails.application.routes.draw do
# nested routes
resources :topics do
resources :posts
end
resources :userdetails
devise_for :users, :controllers => { :registrations => "registrations" }
My Post controller looks like
class PostsController < ApplicationController
# before_action :set_post, only: [:show, :edit, :update, :destroy]
before_filter :has_userdetail_and_topic, :only =>[:new, :create]
# GET /posts
# GET /posts.json
#for new association SAAS book
protected
def has_userdetail_and_topic
unless(#topic =Topic.find_by_id(params[:topic_id]))
flash[:warning] = 'post must be for an existing topic'
end
end
public
def new
#post = #topic.posts.build
###topic = Topic.find(params[:topic_id1])
end
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
##topic.posts << #post
##current_user = current_user.id
#current_user.posts << #topic.posts.build(params[:post])
##post = Post.new(post_params )
##post.userdetail_id = current_user.id
#Association functional between topic and post
#Class variable used
###topic.posts << #post
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:topic_id,:issue, :description, :rating, :userdetail_id)
end
end
I am trying to navigate from topics/index via code <td><%= link_to 'Write', new_topic_post_path(#topic) %> </td>
but when i try to go at localhost:3000/topics]
I am getting error
No route matches {:action=>"new", :controller=>"posts", :topic_id=>nil} missing required keys: [:topic_id]
Can any body tell me about this error, as i am new to rails please clearly specify answer.
And I have one more doubt, please tell me if i am doing association between topic and post incorrectly.I have confusion about this line of code -
#topic.posts << #post
What the error missing required keys: [:topic_id] is telling you is that you need to provide a hash with the key topic_id:
<%= link_to 'Write', new_topic_post_path(topic_id: #topic) %>
Passing a resource as to a route helper only works for the id param:
<%= link_to #topic, topic_path(#topic) %>
Is a kind of shorthand for:
<%= link_to #topic, topic_path(id: #topic.to_param) %>
Addition:
#prcu is also correct. The #topic record needs to be saved to the database. Records which are not saved do not have an id since the database assigns the id column when the record is inserted.
You also need to set the #topic instance variable in PostsController:
#topic = Topic.find(params[:id])
This is commonly done with a before filter:
before_filter :set_topic, only: [:new]
def set_topic
#topic = Topic.find(params[:id])
end
The same also need to be done in TopicsController#index.
#topic is not set or it's not persisted. You can not use topic not saved to db in this helper.