Controller not passing in values from the from to the create method - ruby-on-rails

My Create controller passes in the for the child of a nested resource as null instead of passing the values i've just inputed into the form.
Here is my code
Routes:
resources :trips do
resources :pilgrims
end
Models:
Trip:
class Trip < ActiveRecord::Base
has_many :pilgrims
accepts_nested_attributes_for :pilgrims, :allow_destroy => true
attr_accessible :end_date, :leader_id, :name, :start_date, :pilgrim_attributes
end
Pilgrim:
class Pilgrim < ActiveRecord::Base
belongs_to :trip
attr_accessible :pilgrim_id, :surname, :name, :middle, :aka, :prefix, :address, :city, :state, :zip, :email, :telephone, :nationality, :date_of_birth, :passport_number, :expiration, :jordan, :room, :price, :status, :trip_id
end
My Pilgrim controller:
def new
#trip = Trip.find(params[:trip_id])
#pilgrim = Pilgrim.new
end
def create
#trip = Trip.find(params[:trip_id])
#pilgrim = #trip.pilgrims.build(params[:pilgrim])
if #pilgrim.save
flash[:notice] = "The <b>#{ #pilgrim.name }</b> has been created successfully."
redirect_to(trip_pilgrims_path, :notice => "The <b>#{ #pilgrim.name }</b> ship has been saved successfully.")
else
render(:new, :error => #pilgrim.errors)
end
end
Link to a gist with my form code Form
The routes seem to be correct, when i click on new_trip_pilgrim_path(#trip) it does point to trips/:trip_id/pilgrims/new and loads the new pilgrim form.
However when i click save on the form it redirects me to the route trips/3/pilgrims but shows the new pilgrim form saying all required fields were left blank.
This is what displays in the log.
Started POST "/trips/3/pilgrims" for 127.0.0.1 at 2013-01-19 22:12:06 -0800
Processing by PilgrimsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"kOE06m3DNax43BOLYZ6t1lS7/T4wOWb2xM8m/mlQzvA=", "commit"=>"Create Pilgrim", "trip_id"=>"3"}
Trip Load (0.3ms) SELECT `trips`.* FROM `trips` WHERE `trips`.`id` = 3 LIMIT 1
(0.2ms) BEGIN
(0.2ms) ROLLBACK
Pilgrim Load (0.4ms) SELECT `pilgrims`.* FROM `pilgrims` WHERE `pilgrims`.`trip_id` = 3
Rendered pilgrims/_form.html.erb (36.4ms)
Rendered pilgrims/new.html.erb within layouts/application (37.2ms)
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
Trip Load (0.3ms) SELECT `trips`.* FROM `trips`
Completed 200 OK in 244ms (Views: 102.5ms | ActiveRecord: 11.2ms)
What is going on with the Pilgrim create controller?

You are trying to create a new pilgrim object by calling
#pilgrim = #trip.pilgrims.build(params[:pilgrim])
in the PilgrimsController. But Parameters: {"utf8"=>"✓", "authenticity_token"=>"kOE06m3DNax43BOLYZ6t1lS7/T4wOWb2xM8m/mlQzvA=", "commit"=>"Create Pilgrim", "trip_id"=>"3"} Doesn't contain any pilgrim paramters. So the problem is with the form used for creating a new pilgrim

Related

Rails belongs_to multiple models with accept_nested_attributes_for

I'm building an invoice system for a car trader where each invoice is linked to one customer and one vehicle, with customers potentially having many invoices and vehicles also having many invoices. I have got it working with one nested model doing the following:
purchase_invoice.rb
class PurchaseInvoice < ApplicationRecord
belongs_to :vehicle
accepts_nested_attributes_for :vehicle
end
vehicle.rb
class Vehicle < ApplicationRecord
has_many :purchase_invoices
end
purchase_invoices_controller.rb
def new
#vehicle = Vehicle.new
#purchase_invoice = #vehicle.purchase_invoices.build
end
def create
#vehicle = Vehicle.new
#purchase_invoice = #vehicle.purchase_invoices.build(invoice_params)
if #purchase_invoice.save
redirect_to #purchase_invoice
else
render 'new'
end
end
private
def invoice_params
params.require(:purchase_invoice).permit(:buyer, :location, :vehicle_price, :transfer_fee, :balance_due, :payment_cash, :payment_bank_transfer, :payment_comment, :status, vehicle_attributes: [:vrm, :date_first_registered, :make, :model, :colour, :transmission, :vin, :fuel, :power])
end
new.html.erb
<%= form_with model: #purchase_invoice, local: true do |form| %>
<%= form.fields_for #vehicle do |vehicle_form| %>
<% end %>
<% end %>
However when I add a second relationship like this:
purchase_invoice.rb
class PurchaseInvoice < ApplicationRecord
belongs_to :customer
belongs_to :vehicle
accepts_nested_attributes_for :customer
accepts_nested_attributes_for :vehicle
end
I get an error saying :'Unpermitted parameters: :vehicle'
Does anybody know why? Also, how would I modify the controller new/create action for build whilst maintaining strong params?
I've been Googling this for four hours now and tried a lot but had no luck. Thanks in advance to everybody!
Update
Here's my logs:
Started POST "/purchase_invoices" for 127.0.0.1 at 2018-03-20 15:10:01 +0000
Processing by PurchaseInvoicesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"JB8py9zNxew6aQ6/za3JHDEb4j8f9HGujTlS6P1Eyhb+5NtPPP47fW7AHBkt9eURcnXg0gh9Mf1DCKCSwvlAbg==", "purchase_invoice"=>{"customer"=>{"name"=>""}, "vehicle"=>{"vrm"=>"SA07SSX", "make"=>"VAUXHALL", "model"=>"MERIVA DESIGN", "colour"=>"Silver", "vin"=>"W0L0XCE7574216645", "date_first_registered"=>"20/03/2007"}, "vehicle_odomoter_reading"=>"", "vehicle_number_of_keys"=>"", "vehicle_mot_expiry"=>"", "vehicle_hpi_clear"=>"", "vehicle_comments"=>"", "buyer"=>"", "location"=>"", "vehicle_price"=>"", "transfer_fee"=>"0", "balance_due"=>"", "payment_cash"=>"", "payment_bank_transfer"=>"", "payment_comments"=>""}, "commit"=>"Create Purchase invoice"}
Vehicle Load (0.3ms) SELECT "vehicles".* FROM "vehicles" WHERE "vehicles"."vrm" = ? ORDER BY "vehicles"."id" ASC LIMIT ? [["vrm", "SA07SSX"], ["LIMIT", 1]]
Unpermitted parameters: :customer, :vehicle, :payment_comments
(0.1ms) begin transaction
(0.1ms) rollback transaction
Rendering purchase_invoices/new.html.erb within layouts/application
Rendered purchase_invoices/new.html.erb within layouts/application (10.2ms)
Rendered layouts/_header.html.erb (1.7ms)
Completed 200 OK in 63ms (Views: 54.4ms | ActiveRecord: 0.4ms)
You approach has some issues but you are very close. The PurchaseInvoice model belongs to a Vehicle and a Customer, and it stores a customer_id and a vehicle_id. That's correct. As you said in the comment, it is much more than just a join model, because it holds many other data, such as price, transfer fees, etc. Anyway, you are passing a lot of params regarding the vehicle to be purchased, and not the id of the vehicle. In fact, you are creating the vehicle in the PurchaseInvoice create, which makes no sense. Moreover, your vehicle_attributes should not be an array, but a hash (because PurchaseInvoice belongs_to :vehicle; so it is just one), and should only have the vehicle_id. And giving that you just need the vehicle_id, you do not need nested_attributes (neither for customer) . I would change:
The controller:
def new
#vehicles = Vehicle.all #All vehicles, to select from the list
#customers = Customer.all #All customers, to select from the list (unless you use current_user)
#purchase_invoice = PurchaseInvoice.new
end
def create
#vehicle = Vehicle.find(params[:vehicle_id])
#customer = Customer.find(params[:customer_id]) # Or use current_user
#The above is only needed to check if vehicle and customer exist, but it is not needed below
#purchase_invoice = PurchaseInvoice.create(invoice_params)
if #purchase_invoice.save
redirect_to #purchase_invoice
else
render 'new'
end
end
def invoice_params
params.require(:purchase_invoice).permit(:customer_id, vehicle_id, :location, :vehicle_price, :transfer_fee, :balance_due, :payment_cash, :payment_bank_transfer, :payment_comment, :status)
end
The view:
<%= form_with model: #purchase_invoice, local: true do |form| %>
<!-- This is necessary to choose the customer. If the customer is current_user, just remove it. -->
<%= form.collection_select(:customer_id, #customers, :id, :name %>
<!-- This is necessary to choose the vehicle. This is extremely simplified -->
<!-- The customer should be able to look at many attributes of the car -->
<!-- to be able to select one to purchase -->
<%= form.collection_select(:vehicle_id, #vehicles, :id, :make %>
<!-- All other invoice fields. -->
<% end %>

Meal ordering system and associations

I'm doing meal ordering system in Rails, I made model for User, Restaurant, Meal and Order and I don't know which associations should I use to connect all these models together. For now my models looks like this:
class User < ApplicationRecord
has_many :orders
has_many :restaurants, through: :orders
end
class Order < ApplicationRecord
belongs_to :user
belongs_to :restaurant
end
class Meal < ApplicationRecord
belongs_to :restaurant
end
class Restaurant < ApplicationRecord
has_many :orders
has_many :meals
has_many :users, through: :orders
end
Now when I'm using form to order some meal and save this order in the database I'm getting error in log:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+pwoJ/82k/2SiS7z4X4nVHyaKCMMfWCECQe6TufnkpNaW9PEgvlwxlf3skAH2QQupSLIoe81Z/I0CleL/m9cjw==", "orders"=>{"restaurant_id"=>"2", "meal_id"=>"2", "suggestions"=>""}, "commit"=>"Place your order"}
Unpermitted parameters: restaurant_id, meal_id
(0.1ms) begin transaction
(0.1ms) rollback transaction
Redirected to http://localhost:3000/
Completed 302 Found in 8ms (ActiveRecord: 0.2ms)
My order controller looks like this:
class OrdersController < ApplicationController
def new
#order = Order.new
end
def create
#order = Order.new(order_params)
if #order.save
redirect_to root_path
flash[:success] = "Your order has been added"
else
flash[:danger] = "Error"
redirect_to root_path
end
end
private
def order_params
params.require(:orders).permit(:restaurant, :meal, :suggestions)
end
end
When I change def order_params to:
params.require(:orders).permit(:restaurant_id, :meal_id, :suggestions)
I'm getting
unknown attribute 'restaurant_id' for Order.
I assume that it's bad associations fault, can anyone help me?
* UPDATE *
Now I'm getting error in my log when I'm trying to save order:
Started POST "/new_order" for 127.0.0.1 at 2016-12-19 11:18:50 +0100
Processing by OrdersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"nnDqY0FVzUslTJ1VoL57vnlO6aSTLcVuenT1GJwloJ8+txGAPJoucOAyAeZGGVjEoPYJJnBlwhhHeRjdha1ugw==", "orders"=>{"restaurant_id"=>"4", "meal_id"=>"26", "suggestions"=>""}, "commit"=>"Place your order"}
(0.1ms) begin transaction
Restaurant Load (0.1ms) SELECT "restaurants".* FROM "restaurants" WHERE "restaurants"."id" = ? LIMIT ? [["id", 4], ["LIMIT", 1]]
(0.1ms) rollback transaction
Redirected to http://localhost:3000/
Completed 302 Found in 6ms (ActiveRecord: 0.3ms)
* Update 2 *
When I change for #order.save! I get:
Validation failed: User must exist
With order.errors.inspect I just get:
(0.2ms) rollback transaction
No template found for OrdersController#create, rendering head :no_content
Completed 204 No Content in 97ms (ActiveRecord: 1.8ms)
I'm using omniauth to sign in before user can order a meal and this is only way to sign up or sign in. When I created Order model I used user:references, you think this can be a reason?
You said that you have a sign_in/sign_up, so I assume that you have a current_user in your controller.
In order to save your order you need to supply user_id, here is a one way to do it
def order_params
params.require(:orders).permit(:restaurant, :meal, :suggestions)
.merge(user_id: current_user.id)
end

rails4 has_one association scope with where query

I am developing a Product model that has an has_one association to itself including a raw material record. And the model returns data through REST API. I am using Active Model Serializer instead of JBuilder.
Product model has a 'code' field that contain a product code in string:
'001-000-01-01' (This is a product.)
'001-000-00-01' (This is a material.)
Only difference between two codes is the third number from right. '1' is product. '0' is material. I want to include a "raw_material" record when retrieving a product record. So, I try to set has_one association with scope that has a "where" clause (later I can compose a query to get a material from a product code). Now I simply pass "product" object in lambda and use it in where.
First I write in def raw_material, this works. However, I don't know how to pass an object to def and use it in where clause. Therefore I come up with the scope pattern in has_one, however it returns an error even though it generates exactly the same SELECT as the def pattern. I get "NoMethodError" instead.
class Product < ActiveRecord::Base
has_many :supplies, ->{order('row_order ASC') }, primary_key: :code, foreign_key: :product_code
#This Works!
#has_one :raw_material, class_name: 'Product', primary_key: :code, foreign_key: :code
#This Works!
#has_one :raw_material
#Does not work. Why?
has_one :raw_material, ->(product) { where('code = ?', product.code).take }, class_name: 'Product'
accepts_nested_attributes_for :supplies, allow_destroy: true
#def raw_material
# Product.where('code = ?', '001-000-01-01').take
#end
end
The "def" pattern works:
Started GET "/products/1.json" for ::1 at 2016-10-05 21:48:15 +0900
Processing by ProductsController#show as JSON
Parameters: {"id"=>"1"}
Product Load (0.3ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", 1]]
[active_model_serializers] Supply Load (0.1ms) SELECT "supplies".* FROM "supplies" WHERE "supplies"."product_code" = ? ORDER BY row_order ASC [["product_code", "031-052-00-01"]]
[active_model_serializers] Product Load (0.1ms) SELECT "products".* FROM "products" WHERE (code = '001-000-01-01') LIMIT 1
[active_model_serializers] Rendered ProductSerializer with ActiveModelSerializers::Adapter::Attributes (6.71ms)
Completed 200 OK in 24ms (Views: 9.2ms | ActiveRecord: 1.4ms)
However, the scope pattern does not work:
Started GET "/products/1.json" for ::1 at 2016-10-06 08:19:13 +0900
Processing by ProductsController#show as JSON
Parameters: {"id"=>"1"}
Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", 1]]
[active_model_serializers] Supply Load (0.1ms) SELECT "supplies".* FROM "supplies" WHERE "supplies"."product_code" = ? ORDER BY row_order ASC [["product_code", "031-052-00-01"]]
[active_model_serializers] Product Load (0.1ms) SELECT "products".* FROM "products" WHERE (code = '031-052-00-01') LIMIT 1
[active_model_serializers] Rendered ProductSerializer with ActiveModelSerializers::Adapter::Attributes (10.6ms)
Completed 500 Internal Server Error in 27ms (ActiveRecord: 1.2ms)
NoMethodError (undefined method `except' for #<Product:0x007fe97b090418>):
app/controllers/products_controller.rb:17:in `block (2 levels) in show'
app/controllers/products_controller.rb:14:in `show'
Rendered vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.5/lib/action_dispatch/middleware/templates/rescues/_source.erb (8.8ms)
Product Controller simply defines show like this:
# GET /products/1
# GET /products/1.json
def show
respond_to do |format|
format.html
format.json do
render json: #product, include: ['raw_material']
end
end
end
product_serializer.rb is:
class ProductSerializer < ActiveModel::Serializer
attributes(*Product.attribute_names.map(&:to_sym))
has_one :raw_material
end
Any assistance is greatly appreciated.
Thank you
UPDATE:
I solved this issue myself. Please check my answer below. Thank all who wrote solutions.
You can customise the has_one :raw_material as follows.
class ProductSerializer < ActiveModel::Serializer
attributes(*Product.attribute_names.map(&:to_sym))
has_one :raw_material do
Product.where(code: object.code)
end
end
I could solve this myself. Since I don't have to write this in a line, the def(method) pattern is the best bet. How to override "has_many" could help my issue.
Overriding a has_many association getter
"self" can be used for has_one to get an object inside def.
has_one :raw_material
def raw_material
Product.where('code like ?', (self.code)[0,9] + '_' + (self.code)[10,3]).take
end
This works and can generate any dataset you like in a Model.
# Does not work. Why?
has_one :raw_material ->(product) { where('code = ?', product.code).take }...
because you are trying to define a scope here... not an association. if you want a scope, use a scope eg:
scope :raw_material ->(product) { where('code = ?', product.code).first }
if you want an association, then use an association eg
has_one :raw_material, class_name: 'Product', primary_key: :code, foreign_key: :code
don't try to mix the two.

Creating a Nested Association with Has_Many :Through

I am not sure if my title is properly using the vocabulary. I found a few SO posts where people had similar issues (I couldn't find a solution in those posts), so I used a similar title to those. I am trying to allow users to create clubs and automatically assign the user as a member of the club. However, it seems like I have something out of order when I tried to create the club.
I have three models:
###Models###
#user
has_many :club_memberships, :class_name => 'ClubMembership', :foreign_key => :member_id, :primary_key => :id
has_many :clubs, :through => :club_memberships
#club
attr_accessor :club_name, :club_type
has_many :club_memberships
has_many :members, :through => :club_memberships
#clubmembership
attr_accessor :club_id, :member_id, :membership_type
belongs_to :club
belongs_to :member, :class_name => "User", :foreign_key => :member_id, :primary_key => :id
Here are the relevant parts of the controllers
###Controllers###
#Clubs
def new
#club = Club.new
end
def create
#club = Club.new(club_params)
if #club.save
#club_membership = ClubMembership.create(
member_id: current_user.id,
club_id: #club.id,
membership_type: 'founder'
)
flash[:success] = "Club Created!"
redirect_to root_url
else
render #club.errors.full_messages
end
end
private
def club_params
params.require(:club).permit(:club_name, :club_type)
end
#ClubMemberships
def create
#club_membership = ClubMembership.new(club_membership_params)
if #club_membership.save
render #club_membership
else
render #club_membership.errors.full_messages
end
end
private
def club_membership_params
params.require(:club_membership).permit(:member_id, :club_id, :membership_type)
end
My form_for
###View###
#club#new
= form_for(#club) do |f|
.field.center-align
= f.label :club_name
= f.text_field :club_name, :class => "form-control fieldbox", autofocus: true
.field.center-align
= f.label :club_type
= f.text_field :club_type, :class => 'form-control fieldbox', autofocus: true
.actions.center-align
= f.submit "Create Club!", :class => "btn hoverable padtop"
And finally, here is what the log shows on post
#log
Started POST "/clubs" for at 2015-09-03 22:32:41 +0000
Cannot render console from ! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by ClubsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Ar2dv41/Tqk9EVjwfLLeD8bnpLoVWQIdDxG3Ju1GO3stLLvPd/FFgoFF9YuHobWbgb2byqkgAMiWRJAg5YcGKQ==", "club"=>{"club_name"=>"Test Club", "club_type"=>"Test Type"}, "commit"=>"Create Club!"}
(0.2ms) BEGIN
Club Exists (0.4ms) SELECT 1 AS one FROM `clubs` WHERE `clubs`.`club_name` = BINARY 'Test Club' LIMIT 1
SQL (0.3ms) INSERT INTO `clubs` (`created_at`, `updated_at`) VALUES ('2015-09-03 22:32:41', '2015-09-03 22:32:41')
(3.4ms) COMMIT
User Load (0.1ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 56 LIMIT 1
(0.1ms) BEGIN
ClubMembership Exists (0.3ms) SELECT 1 AS one FROM `club_memberships` WHERE (`club_memberships`.`member_id` = BINARY 56 AND `club_memberships`.`club_id` IS NULL) LIMIT 1
SQL (1.6ms) INSERT INTO `club_memberships` (`created_at`, `updated_at`) VALUES ('2015-09-03 22:32:41', '2015-09-03 22:32:41')
(3.1ms) COMMIT
Redirected to c9
Completed 302 Found in 71ms (ActiveRecord: 11.1ms)
I'll be honest. I have no clue what is happening in that POST or where to tackle this from next. It seems like the parameters I want are going through, but then they aren't. Any help would be appreciated.
I figured it out. I tried two things at once and it worked, so I can't say for sure what helped, but I am pretty sure the second thing was most important.
First, I removed the attr_accessor from both model's (Club and ClubMembership). Why did I put it in there in the first place? No idea. I probably saw it somewhere and thought it was cool.
Second, also in the models, I had validators that I didn't post because I didn't think they were important. I had:
validates :club_name, :club_type, :presence => true
validates :club_name, :uniqueness => true
I changed this to:
validates :club_name, presence: true
validates :club_type, presence: true
Turns out that was important and everything is working fine now.

Deletion of nested attributes does not work

I seem to be unable to delete items using the accepts_nested_attributes_for command but I have done this according to this tutorial and the associated git repo. My models are ...
class Job < ActiveRecord::Base
has_many :specialties, :inverse_of => :job
accepts_nested_attributes_for :specialties, allow_destroy: true, :reject_if => :all_blank
end
class Specialty < ActiveRecord::Base
belongs_to :job, :inverse_of => :specialties
end
In my Job form, I have...
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Specialty" %>
When I click the checkbox to delete the a couple of specialties, nothing happens. I checked the server output and received:
Started PATCH "/jobs/1" for 127.0.0.1 at 2013-07-16 16:15:16 -0400
Processing by JobsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8VxYXujcKXpLEm8+7B43SLU6X3fH00kIOmFK+nvaBKs=", "job"=>{"name"=>"Cook", "description"=>"Makes the food.", "specialties_attributes"=>{"2"=>{"name"=>"", "description"=>"", "_destroy"=>"1", "id"=>"3"}, "3"=>{"name"=>"", "description"=>"", "_destroy"=>"1", "id"=>"4"}}}, "commit"=>"Update Job", "id"=>"1"}
Job Load (0.1ms) SELECT "jobs".* FROM "jobs" WHERE "jobs"."id" = ? LIMIT 1 [["id", "1"]]
Unpermitted parameters: _destroy
Unpermitted parameters: _destroy
Unpermitted parameters: _destroy
Unpermitted parameters: _destroy
What did I miss? I've gone through the tutorial and repo a bunch of times and I can't see where I've gone off.
That's because of strong_parameters. You now have to permit keys. In your action:
params.permit(:_destroy)
I like #Damien Roche's answer but its just for not confusion to any one. follow this
In job_controller.rb
private
def job_params
params.require(:job).permit(:id, :description, :specialty_fields_attributes => [:id, :job_id, :avatar, :_destroy])
end

Resources