Rails assign id in model - ruby-on-rails

I would like to assign the event_option_id to the registration. I can easily do it in the view by adding this to the form:
<%= f.text_field :event_option_id, value: #event_option.id %>
I would like to do it in the model not in the view. For security I'm doing the same for the registration price. Setting the price from the model is working but doing the same thing for the event_option_id is not.
Registration Model:
class Registration < ActiveRecord::Base
belongs_to :event_option
belongs_to :order_item
belongs_to :order
before_save :set_event_options
def order_present
if order.nil?
errors.add(:order, "is not a valid order.")
end
end
def registration_price
self[:price] = event_option.price
end
def event_option_id
self.event_option_id = event_option
end
private
def set_event_options
self[:price] = registration_price
self.event_option_id = event_option_id
end
end
EventOptions model:
class EventOption < ActiveRecord::Base
belongs_to :event
has_many :registrations
end
Create Method in the Registrations controller:
def create
#event_option = EventOption.find(params[:id])
#order = current_order
#registration = #order.registrations.build(registration_params)
##registration = Registration.new(registration_params)
#order_id = current_order.id
respond_to do |format|
if #registration.save
format.html { redirect_to #registration, notice: 'Registration was successfully created.' }
format.json { render :show, status: :created, location: #registration }
format.js {}
#order.save
session[:order_id] = #order.id
else
format.html { render :new }
format.json { render json: #registration.errors, status: :unprocessable_entity }
end
end
Error in log:
Started POST "/registrations" for 127.0.0.1 at 2016-01-04 21:16:06 -0500
Processing by RegistrationsController#create as JS
Parameters: {"utf8"=>"âo"", "registration"=>{"name"=>"saasas", "lastname"=>"asas"}, "commit"=>"Create Registration"}
EventOption Load (0.0ms) SELECT "event_options".* FROM "event_options" WHERE "event_options"."id" = ? LIMIT 1 [["id", nil]]
Completed 404 Not Found in 8ms (ActiveRecord: 0.0ms)
ActiveRecord::RecordNotFound (Couldn't find EventOption with 'id'=):
app/controllers/registrations_controller.rb:27:in `create'
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/_source.erb (0.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.2.4/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.2.4/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb (1.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb (44.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/web-console-2.2.1/lib/web_console/templates/_markup.html.erb (1.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/web-console-2.2.1/lib/web_console/templates/_inner_console_markup.html.erb within layouts/inlined_string (0.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/web-console-2.2.1/lib/web_console/templates/_prompt_box_markup.html.erb within layouts/inlined_string (0.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/web-console-2.2.1/lib/web_console/templates/style.css.erb within layouts/inlined_string (0.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/web-console-2.2.1/lib/web_console/templates/console.js.erb within layouts/javascript (48.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/web-console-2.2.1/lib/web_console/templates/main.js.erb within layouts/javascript (0.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/web-console-2.2.1/lib/web_console/templates/error_page.js.erb within layouts/javascript (0.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/web-console-2.2.1/lib/web_console/templates/index.html.erb (105.0ms)
I'm reading this part of the rails documentation:
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html,
but still can't figure out whats going on.
Update:
Routes:
Rails.application.routes.draw do
resource :cart, only: [:show]
resources :orders
resources :order_items
resources :registrations
resources :event_options
resources :events
resources :charges
root 'events#index'
Registration form - inside event_option show.html.erb:
<%= form_for(#registration, remote: true) do |f| %>
<% if #registration.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#registration.errors.count, "error") %> prohibited this registration from being saved:</h2>
<ul>
<% #registration.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>
<div class="field">
<%= f.label :lastname %><br>
<%= f.text_field :lastname %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>

I think your controller's create function is missing params[:id].
The easiest way to fix is adding it into your form:
<%= form_for(#registration, remote: true) do |f| %>
<%= hidden_field_tag :id, your_event_option_id %>
### your other stuffs

The error clearly states that Rails is unable to find an EventOption without an id:
def create
#event_option = EventOption.find params[:id] #-> :id is not passed to create action
To fix it, just use the param that's submitted as part of the form:
#app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
def create
#order = current_order
#registration = #order.registrations.new registration_params
#registration.save
end
private
def registration_params
params.require(:registration).permit(:event_option_id, :other, :params)
end
end
--
The above would work well if the user could choose the event_option_id in the form; if you're using a hidden_field, you'll be better using nested routes:
#config/routes.rb
resources :event_option do
resources :registrations #-> url.com/event_options/:event_option_id/registrations/new
end
This will set the event_option_id as part of the top-level params hash, which will be passed to the controller as params[:event_option_id] (as you have it already):
#app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
def create
#event_option = EventOption.find params[:event_option_id]
end
end
Tip - you can declare multiple resources at once:
#config/routes.rb
resource :cart, only: [:show]
resources :orders, :order_items, :registrations, :event_options, :events, :charges

Related

Rails 5 Independent View Nested Resource - param is missing or the value is empty

I have a nested resource called PracticeQuestion, which is a child of PracticeQuiz. I want users to be able to go through one question at a time when they are at a PracticeQuiz. For example: foo.com/practice_quizzes/1/practice_questions/1..n
I got the practice quizzes working, but when I try to add a new practice question, I get a rails error that says that the param is missing or empty, but I don't see what i'm doing wrong. Please help
practice_quiz.rb
class PracticeQuiz < ApplicationRecord
belongs_to :user, optional: true
validates :user, presence: true
has_many :practice_questions, dependent: :destroy
end
practice_question.rb
class PracticeQuestion < ApplicationRecord
belongs_to :user, optional: true
belongs_to :practice_quiz
end
practice_questions_controller.rb
class PracticeQuestionsController < ApplicationController
before_action :set_practice_question, only: [:show, :edit, :update, :destroy]
def index
#practice_questions = PracticeQuestion.all
end
def show
end
# GET /practice_questions/new
def new
#practice_quiz = PracticeQuiz.friendly.find(params[:practice_quiz_id])
#practice_question = PracticeQuestion.new
end
# GET /practice_questions/1/edit
def edit
end
def create
#practice_quiz = PracticeQuiz.friendly.find(params[:practice_quiz_id])
#practice_question = PracticeQuestion.new(practice_question_params)
respond_to do |format|
if #practice_question.save
format.html { redirect_to #practice_question, notice: 'Practice question was successfully created.' }
format.json { render :show, status: :created, location: #practice_question }
else
format.html { render :new }
format.json { render json: #practice_question.errors, status: :unprocessable_entity }
end
end
end
private
def set_practice_question
#practice_question = PracticeQuestion.find(params[:id])
end
def practice_question_params
params.require(:practice_question).permit(:question, :explanation, :flagged)
end
end
views/practice_questions/_form.html.erb
<%= form_with(url: practice_quiz_practice_questions_path, local: true) do |form| %>
<% if practice_question.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(practice_question.errors.count, "error") %> prohibited this practice_question from being saved:</h2>
<ul>
<% practice_question.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :question %>
<%= form.text_field :question %>
</div>
<div class="field">
<%= form.label :explanation %>
<%= form.text_field :explanation %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
routes.rb
resources :practice_quizzes do
resources :practice_questions
end
I set the practice_questions controller's new method to find the id of the parent resource, but I get this error. I'm pretty sure I'm following rails naming conventions fine too.
ActionController::ParameterMissing at /practice_quizzes/23535/practice_questions
param is missing or the value is empty: practice_question
Update: here's the results from the rails server window
Processing by PracticeQuestionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"7dpxBB7jjZWzicCYWGA8yeTPSc9UeaqNDOavKQai2vMISryPBiMZ9Zo4LLS3DgZQI8IJc7rLh2TXd9Fj8PAjiA==", "question"=>"235235", "explanation"=>"25235232", "commit"=>"Save ", "practice_quiz_id"=>"23535"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
PracticeQuiz Load (0.9ms) SELECT "practice_quizzes".* FROM "practice_quizzes" WHERE "practice_quizzes"."slug" = $1 ORDER BY "practice_quizzes"."id" ASC LIMIT $2 [["slug", "23535"], ["LIMIT", 1]]
Completed 400 Bad Request in 102ms (ActiveRecord: 1.7ms)
ActionController::ParameterMissing - param is missing or the value is empty: practice_question:
app/controllers/practice_questions_controller.rb:76:in `practice_question_params'
app/controllers/practice_questions_controller.rb:30:in `create'
Update 2:
views/practice_questions/new.html.erb
<h1>New Practice Question</h1>
<%= render 'form', practice_question: #practice_question %>
Here
def practice_question_params
params.require(:practice_question).permit(:question, :explanation, :flagged)
end
you are using rails strong parameters. See this answer https://stackoverflow.com/a/30826895/2627121
Basically, params.require(:practice_question) means that you must have practice_question parameter.
Here
Parameters: {"utf8"=>"✓", "authenticity_token"=>"", "question"=>"235235", "explanation"=>"25235232", "commit"=>"Save ", "practice_quiz_id"=>"23535"}
you have question and explanation as root parameters, when according to your strong parameters declaration you must have
"practice_question" => { "question"=>"235235", "explanation"=>"25235232" }
You should edit form fields to have name as practice_question[question]

Why Rails 5 is not saving nested attributes because parent model is not saving first

I am using Rails 5 and everything at its newest stable versions. So I get the following :
You have your association set to required but it's missing.
Associations are set to required by default in rails 5 so if you want
to keep one empty you need to set optional:true on your association in
mode
This is great and I understand what is going on however for the life of me I cannot figure out how to get the parent model to save first so the user_id is translated the nested models record. I see the same answer above everywhere however no one explains a work around other than turning the default in the initializer from true to false. THIS DOES NOT SOLVE THE PROBLEM, because the record sure does save but it does not include the user_id.
Below is what I have for my code base, I would ask rather than responding with the above quote, could someone enlighten me on HOW to get the USER_ID field into the nested attributes while saving. I refuse to disable validation and manually handle the insertion, as this is not the ruby way and breaks from standards!
Thanks in advance for anyone who can answer this question directly and without vague explanations that digress from the ruby way of things!
###Models
#Users
class User < ApplicationRecord
has_one :profile, inverse_of: :user
accepts_nested_attributes_for :profile, allow_destroy: true
end
#Profiles
class Profile < ApplicationRecord
belongs_to :user, inverse_of: :profile
end
###Controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
#user.build_profile
end
# GET /users/1/edit
def edit
#user.build_profile
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:username, :password, :user_type_id, profile_attributes: [:user_id, :first_name, :middle_name, :last_name, :phone_number, :cell_number, :email])
end
end
##View
<%= form_for(#user) do |f| %>
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
<!--<li><%= debug f %></li>-->
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %>
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :password %>
<%= f.text_field :password %>
</div>
<div class="field">
<% if params[:trainer] == "true" %>
<%= f.label :user_type_id %>
<%= f.text_field :user_type_id, :readonly => true, :value => '2' %>
<% else %>
<%= f.label :user_type_id %>
<%= f.text_field :user_type_id, :readonly => true, :value => '1' %>
<% end %>
</div>
<h2>Account Profile</h2>
<%= f.fields_for :profile do |profile| %>
<%#= profile.inspect %>
<div>
<%= profile.label :first_name %>
<%= profile.text_field :first_name %>
</div>
<div>
<%= profile.label :middle_name %>
<%= profile.text_field :middle_name %>
</div>
<div>
<%= profile.label :last_name %>
<%= profile.text_field :last_name %>
</div>
<div>
<%= profile.label :email %>
<%= profile.text_field :email %>
</div>
<div>
<%= profile.label :phone_number %>
<%= profile.telephone_field :phone_number %>
</div>
<div>
<%= profile.label :cell_phone %>
<%= profile.telephone_field :cell_number %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<%= debug params %>
<%= debug user %>
<%= debug user.profile %>
<% end %>
UPDATE
For starters I have figured out that you need to include autosave: true to the relationship like so
class User < ApplicationRecord
has_one :profile, inverse_of: :user, autosave: true
accepts_nested_attributes_for :profile, allow_destroy: true
end
Then the parent record gets saved before the child. Now comes another gotcha that I am just not sure about and is odd when the form is submitted you will notice in the console output I pasted below that the INSERT INTO profiles statement includes the user_id column and the value of 1. It passees validation and looks like it runs properly from the output, however the user_id column in the profiles table is still null. I am going to keep digging, hopefuly one of my fellow rubyiests out there will see this and have some ideas on how to finish fixing this. I love Rails 5 improvements so far but it wouldn't be ROR without small interesting gotchas! Thanks again in advance!
Started POST "/users" for 192.168.0.31 at 2017-03-12 22:28:14 -0400
Cannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"YA7kQnScvlIBy5OiT+BmOQ2bR7J00ANXId38FqNwX37Cejd+6faUyD3rMF4y0qJNKBUYGaxrRZqcLrXonL6ymA==", "user"=>{"username"=>"john", "password"=>"[FILTERED]", "user_type_id"=>"1", "profile_attributes"=>{"first_name"=>"john", "middle_name"=>"r", "last_name"=>"tung", "email"=>"thegugaru#gmail.com", "phone_number"=>"8033207677", "cell_number"=>"8033207677"}}, "commit"=>"Create User"}
(0.1ms) BEGIN
SQL (0.3ms) INSERT INTO `users` (`username`, `password`, `user_type_id`, `created_at`, `updated_at`) VALUES ('john', '0000', 1, '2017-03-13 02:28:14', '2017-03-13 02:28:14')
SQL (0.4ms) INSERT INTO `profiles` (`user_id`, `email`, `first_name`, `middle_name`, `last_name`, `phone_number`, `cell_number`, `created_at`, `updated_at`) VALUES (1, 'thegu#gmail.com', 'john', 'r', 'tung', '8033207677', '8033207677', '2017-03-13 02:28:14', '2017-03-13 02:28:14')
(10.8ms) COMMIT
Redirected to http://192.168.0.51:3000/users/1
Completed 302 Found in 24ms (ActiveRecord: 11.5ms)
Ok, I am answering my own question because I know many people are struggling with this and I actually have the answer and not a vague response to the documentation.
First we will just be using a one to one relationship for this example. When you create your relationships you need to make sure that the parent model has the following
inverse_of:
autosave: true
accepts_nested_attributes_for :model, allow_destroy:true
Here is the Users model then I will explain,
class User < ApplicationRecord
has_one :profile, inverse_of: :user, autosave: true
accepts_nested_attributes_for :profile, allow_destroy: true
end
in Rails 5 you need inverse_of: because this tells Rails that there is a relationship through foreign key and that it needs to be set on the nested model when saving your form data.
Now if you were to leave autosave: true off from the relationship line you are left with the user_id not saving to the profiles table and just the other columns, unless you have validations off and then it won't error out it will just save it without the user_id.
What is going on here is autosave: true is making sure that the user record is saved first so that it has the user_id to store in the nested attributes for the profile model.
That is it in a nutshell why the user_id was not traversing to the child and it was rolling back rather than committing.
Also one last gotcha is there are some posts out there telling you in your controller for the edit route you should add #user.build_profile like I have in my post. DO NOT DO IT THEY ARE DEAD WRONG, after assessing the console output it results in
Started GET "/users/1/edit" for 192.168.0.31 at 2017-03-12 22:38:17 -0400
Cannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by UsersController#edit as HTML
Parameters: {"id"=>"1"}
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Profile Load (0.5ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`user_id` = 1 LIMIT 1
(0.1ms) BEGIN
SQL (0.5ms) UPDATE `profiles` SET `user_id` = NULL, `updated_at` = '2017-03-13 02:38:17' WHERE `profiles`.`id` = 1
(59.5ms) COMMIT
Rendering users/edit.html.erb within layouts/application
Rendered users/_form.html.erb (44.8ms)
Rendered users/edit.html.erb within layouts/application (50.2ms)
Completed 200 OK in 174ms (Views: 98.6ms | ActiveRecord: 61.1ms)
If you look it is rebuilding the profile from scratch and resetting the user_id to null for the record that matches the current user you are editing.
So be very careful of this as I have seen tons of posts making this suggestion and it cost me DAYS of research to find a solution!

How to manually save a reference object in Rails

Edit: Main problem was that when I added the reference fields, I did theater:reference and not theater:references so the field was not marked as a foreign key. Once I undid those migrations and redid them correctly, I was able to make this work.
In my showtimes controller, I am trying to automatically set the theater id to whatever theater owns the screen that the user inputed, but when I try to save it as an integer or a string, I get an error. Yet, when I try to save it as a theater object, I get "Unpermitted parameter: theater" from the console and a "Theater must exist" error from the rails application.
showtimes_controller:
class ShowtimesController < ApplicationController
before_action :set_theater, only: [:create, :edit]
before_action :set_showtime, only: [:show, :edit, :update, :destroy]
# GET /showtimes
# GET /showtimes.json
def index
#showtimes = Showtime.all
end
# GET /showtimes/1
# GET /showtimes/1.json
def show
end
# GET /showtimes/new
def new
#showtime = Showtime.new
end
# GET /showtimes/1/edit
def edit
end
# POST /showtimes
# POST /showtimes.json
def create
#showtime = Showtime.new(showtime_params)
respond_to do |format|
if #showtime.save
format.html { redirect_to #showtime, notice: 'Showtime was successfully created.' }
format.json { render :show, status: :created, location: #showtime }
else
format.html { render :new }
format.json { render json: #showtime.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /showtimes/1
# PATCH/PUT /showtimes/1.json
def update
respond_to do |format|
if #showtime.update(showtime_params)
format.html { redirect_to #showtime, notice: 'Showtime was successfully updated.' }
format.json { render :show, status: :ok, location: #showtime }
else
format.html { render :edit }
format.json { render json: #showtime.errors, status: :unprocessable_entity }
end
end
end
# DELETE /showtimes/1
# DELETE /showtimes/1.json
def destroy
#showtime.destroy
respond_to do |format|
format.html { redirect_to showtimes_url, notice: 'Showtime was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_showtime
#showtime = Showtime.find(params[:id])
end
def set_theater
screenInfo = Screen.where("id = ?", params[:showtime][:screen])
params['showtime']['theater'] = Theater.find(screenInfo[0]['theater_id'])
end
# Never trust parameters from the scary internet, only allow the white list through.
def showtime_params
params.require(:showtime).permit(:date, :time, :archived, :movie_id, :theater, :screen)
end
end
showtimes model:
class Showtime < ApplicationRecord
belongs_to :movie
belongs_to :theater
end
Showtimes _form
<%= form_for(showtime) do |f| %>
<% if showtime.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(showtime.errors.count, "error") %> prohibited this showtime from being saved:</h2>
<ul>
<% showtime.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :date %>
<%= f.date_select :date %>
</div>
<div class="field">
<%= f.label :time %>
<%= f.time_select :time %>
</div>
<div class="field">
<%= f.label :archived %>
<%= f.check_box :archived %>
</div>
<div class="field">
<%= f.label :movie_id %>
<%= f.text_field :movie_id %>
</div>
<div class="field">
<%= f.label :screen %>
<%= f.text_field :screen %>
</div>
<%= f.hidden_field :theater, :value => "" %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Error when trying to save as integer:
Theater(#70015922237640) expected, got Fixnum(#11723820)
Error when trying to save as string:
Theater(#70015868755420) expected, got String(#11739240)
Logs when trying to save as Theater object:
Started POST "/showtimes" for IP at 2016-11-08 20:22:37 +0000
Processing by ShowtimesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"nENPV5d6YRXdcx3H+Xa9ZypGtyFlaTg+zyENGB10TmW9OyWxLR9Dsl7nDoG9irq+3qApiNA2/oEqL5RZ0SXorA==", "showtime"=>{"date(1i)"=>"2016", "date(2i)"=>"11", "date(3i)"=>"8", "time(1i)"=>"2016", "time(2i)"=>"11", "time(3i)"=>"8", "time(4i)"=>"20", "time(5i)"=>"22", "archived"=>"0", "movie_id"=>"2", "screen"=>"1", "theater"=>""}, "commit"=>"Create Showtime"}
[1m[36mScreen Load (0.3ms)[0m [1m[34mSELECT "screens".* FROM "screens" WHERE (id = '1')[0m
[1m[36mTheater Load (0.2ms)[0m [1m[34mSELECT "theaters".* FROM "theaters" WHERE "theaters"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
Unpermitted parameter: theater
[1m[35m (0.1ms)[0m [1m[36mbegin transaction[0m
[1m[36mMovie Load (0.2ms)[0m [1m[34mSELECT "movies".* FROM "movies" WHERE "movies"."id" = ? LIMIT ?[0m [["id", 2], ["LIMIT", 1]]
[1m[35m (0.2ms)[0m [1m[31mrollback transaction[0m
Rendering showtimes/new.html.erb within layouts/application
Rendered showtimes/_form.html.erb (13.6ms)
Rendered showtimes/new.html.erb within layouts/application (16.4ms)
Completed 200 OK in 323ms (Views: 86.5ms | ActiveRecord: 3.9ms)
How the hell do I save this parameter?
Have you tried assigning your object to an instance variable, and assigning it before saving?
On your before_action
def set_theater
#theather = ... # Code to find the theather
end
On your create action
def create
#showtime = Showtime.new(showtime_params)
#showtime.theather = #theather
... # Code to save and handle errors
end
You use theater instead of theater_id in several places in your code, and you'll need to change it in all the places, in order for this to work.
Firstly - you can't select a theater in our form... html doesn't recognise a type of theaterand will not pass one through - so your form needs to pass the theater_id instead (which will be an integer that it happily can deal with).
# eg here make sure it's a theater_id
<%= f.hidden_field :theater_id, :value => #theater.id %>
next - your require/permit is probably what's throwing some errors - you need that to be theater_id as well:
def showtime_params
params.require(:showtime).permit(:date, :time, :archived, :movie_id, :theater_id, :screen)
end
Now you need to fetch the theater out, using the screen-info param - but also keep in mind that this might come through as nil some times (so a guard-clause is always good):
def set_theater
if params[:showtime].present? && params[:showtime][:screen_id].present?
screen_info = Screen.find(params[:showtime][:screen_id])
#theater = Theater.find(screenInfo.theater_id)
end
end
Note: I have updated naming-schemes to be rail-standard and removed the thing where you try to set the theatre in params as below:
params['showtime']['theater'] = Theater.find(screenInfo[0]['theater_id'])
I don't know what you're actually trying to do with this line of code, but whatever it is, params doesn't work that way - consider that params is "the set of things that were passed through to us from the user, and are then thrown away" - we don't use it to store new values that we create ion the controller. That's what #variables are for
Can you explain more what you're trying to do and we'll figure out the right way to do it :)

Creating nested models in Rails 4 forum app

Hello I am making a Forum application in Rails 4. It can have numerous forums, each with numerous topics. Each topic can have many posts. When creating a new topic, one must also create the initial post, much like Stack Overflow itself. Therefore, I have a text area in the "New Topic" form that allows this with a fields_for method. The Problem is, when you click the "Create Topic" button after filling out the form (including the "post" field), the transaction is rolled back. The following validation error appears:
3 errors prohibited this topic from being saved:
Posts forum must exist
Posts topic must exist
Posts user must exist
This is my form: app/views/topics/_form.html.erb
<%= form_for([ #forum, topic ]) do |f| %>
<% if topic.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(topic.errors.count, "error") %> prohibited this topic from being saved:</h2>
<ul>
<% topic.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.fields_for :posts do |builder| %>
<%= builder.label :content %><br>
<%= builder.cktext_area :content, class: 'ckeditor' %>
<% end %>
</div>
<div class="actions">
<%= f.submit 'Create Topic', class: "btn btn-l btn-success" %>
</div>
<% end %>
Models: forum.rb
class Forum < ApplicationRecord
has_many :topics, dependent: :destroy
has_many :posts, through: :topics
def most_recent_post
topic = Topic.last
return topic
end
end
topic.rb
class Topic < ApplicationRecord
belongs_to :forum
belongs_to :user
has_many :posts, dependent: :destroy
accepts_nested_attributes_for :posts
end
post.rb
class Post < ApplicationRecord
belongs_to :forum
belongs_to :topic
belongs_to :user
validates :content, presence: true
end
The controller for topics, app/controllers/topics_controller.rb
class TopicsController < ApplicationController
before_action :get_forum
before_action :set_topic, only: [:show, :edit, :update, :destroy]
# GET /topics
# GET /topics.json
def index
#topics = #forum.topics
end
# GET /topics/1
# GET /topics/1.json
def show
end
# GET /topics/new
def new
#topic = #forum.topics.build
#topic.posts.build
end
# GET /topics/1/edit
def edit
# #topic.posts.build
end
# POST /topics
# POST /topics.json
def create
#topic = #forum.topics.build(topic_params.merge(user_id: current_user.id))
#topic.last_poster_id = #topic.user_id
respond_to do |format|
if #topic.save
format.html { redirect_to forum_topic_path(#forum, #topic), notice: 'Topic was successfully created.' }
format.json { render :show, status: :created, location: #topic }
else
format.html { render :new }
format.json { render json: #topic.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /topics/1
# PATCH/PUT /topics/1.json
def update
respond_to do |format|
if #topic.update(topic_params)
format.html { redirect_to forum_topic_path(#forum, #topic), notice: 'Topic was successfully updated.' }
format.json { render :show, status: :ok, location: #topic }
else
format.html { render :edit }
format.json { render json: #topic.errors, status: :unprocessable_entity }
end
end
end
# DELETE /topics/1
# DELETE /topics/1.json
def destroy
#topic.destroy
respond_to do |format|
format.html { redirect_to forum_path(#forum), notice: 'Topic was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def get_forum
#forum = Forum.find(params[:forum_id])
end
def set_topic
#topic = Topic.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def topic_params
params.require(:topic).permit(:title, :last_poster_id, :last_post_at, :tags, :forum_id, :user_id, posts_attributes: [:id, :content])
end
end
As you see I've added the posts_attributes to the strong parameters for topic. These are the only fields that posts have besides the foreign key references (:forum_id, :topic_id, :user_id). And I've tried putting those attributes in, but I get the same error.
Finally, this is my routes.rb
Rails.application.routes.draw do
resources :forums do
resources :topics do
resources :posts
end
end
resources :sessions
resources :users
mount Ckeditor::Engine => '/ckeditor'
end
I should also mention that I have tried adding hidden_fields inside of fields_for, with the id criteria for #forum, #topic, and current_user. That throws the same validation error.
What am I missing? I feel like it's something in the controller. Like I'm not saving it properly. Every tutorial I've seen has it this way. Except for the Rails <=3 versions, which are way different because of no strong_params.
Any ideas? Thanks for the help!
EDIT Here is the log output when I try to submit a topic entitled "I am a title" and the content "I am some content"...
Started POST "/forums/1/topics" for 127.0.0.1 at 2016-01-31 09:03:33 -0500
Processing by TopicsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"pYt842XQHiOKqNjPHBO8lNP2z92gHF7Lpt24CppbuvHR/cFHky3FVCpBs77p7WFRKmYBHgeZQjx0sE+DI+Q+sQ==", "topic"=>{"title"=>"I am a title", "posts_attributes"=>{"0"=>{"content"=>"<p>I am some content</p>\r\n"}}}, "commit"=>"Create Topic", "forum_id"=>"1"}
Forum Load (0.6ms) SELECT "forums".* FROM "forums" WHERE "forums"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.3ms) BEGIN
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.4ms) ROLLBACK
This is not a direct answer; too long for comment.
One of the issues you have with your routes is that you're nesting too many resources:
Resources should never be nested more than 1 level deep...
resources :x do
resources :y
end
--
Although you can do what you're doing, it would perhaps be better to use a scope:
#config/routes.rb
scope ':forum' do
resources :topics do
resources :posts
end
end
The issue you're facing is that things can get very complicated, very quickly. Although the
This way, you could make the forums CRUD accessible in its own set of functionality:
#config/routes.rb
resources :forums #-> only accessible to admins?
scope ...
Either way, you'd still need to define your routes with the forum present:
<%= link_to "Test", [#forum, #topic, #post] %>

Embed a Rails form partial into another page

I'm building a rails 4.2.0 app with a contact us page (this page does have a semi-empty controller). I'm trying to embed a form partial from another controller.
Here is the code (minus the text):
<% if user_signed_in? %>
<% render 'enquiries/form' %>
<% end %>
When I run this I get the error 'First argument in form cannot contain nil or be empty'.
My enquiries form looks like a basic rails form:
<%= form_for #enquiry do |f| %>
<% if #enquiry.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#enquiry.errors.count, "error") %> prohibited this enquiry from being saved:</h2>
<ul>
<% #enquiry.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :subject, "Subject:" %><br>
<%= f.text_field :subject %>
</div>
<div class="field">
<%= f.label :e_description, "Description:" %><br>
<%= f.text_area :e_description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
What could be the possible reason for the error? Or is there a better way of embedding a view into another?
Update/Edit:
Here's the routes:
devise_for :users
resources :rooms do
resources :viewings
end
resources :rmcats
resources :extras
resources :extracats
resources :enquiries
root :to => redirect('/pages/home')
get 'pages/home'
get 'pages/contactus'
And the enquiry controller:
class EnquiriesController < ApplicationController
before_action :set_enquiry, only: [:show, :edit, :update, :destroy]
# GET /enquiries
def index
#enquiries = Enquiry.all
end
# GET /enquiries/1
def show
end
# GET /enquiries/new
def new
#enquiry = Enquiry.new
end
# GET /enquiries/1/edit
def edit
end
# POST /enquiries
def create
#enquiry = Enquiry.new(enquiry_params)
if #enquiry.save
redirect_to #enquiry, notice: 'Enquiry was successfully created.'
else
render :new
end
end
# PATCH/PUT /enquiries/1
def update
if #enquiry.update(enquiry_params)
redirect_to #enquiry, notice: 'Enquiry was successfully updated.'
else
render :edit
end
end
# DELETE /enquiries/1
def destroy
#enquiry.destroy
redirect_to enquiries_url, notice: 'Enquiry was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_enquiry
#enquiry = Enquiry.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def enquiry_params
params.require(:enquiry).permit(:subject, :e_description)
end
end
This is the pages controller:
class PagesController < ApplicationController
around_filter :resource_not_found
# def home
# end
private
# If resource not found redirect to root and flash error.
# => For pages this will rarely be needed as it should 404.
def resource_not_found
yield
rescue ActiveRecord::RecordNotFound
redirect_to root_url, :notice => "Page not found."
end
end
Edit:
Log:
Started GET "/pages/contactus" for ::1 at 2015-03-21 01:05:25 +0000
Processing by EnquiriesController#new as HTML
[1m[35mUser Load (0.0ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Rendered enquiries/_form.html.erb (0.0ms)
Rendered pages/contactus.html.erb within layouts/application (0.0ms)
Completed 200 OK in 235ms (Views: 234.6ms | ActiveRecord: 0.0ms)
It is telling you that #enquiry is nil at the time it is trying to render the form. You need to call the new action to create the #enqiury for the form to represent.
You could change your route to:
get 'pages/contactus' => 'enquiries#new'
Then in your Enquiry controller:
def new
#enquiry = Enquiry.new
render 'pages/contactus'
end
EDIT:
Ok, so now we combine what Friends Systems put in his answer:
<% if user_signed_in? %>
<%= render 'enquiries/form' enquiry: #enquiry %>
<% end %>
And now change any instance of #enquiry in the form to enquiry
This is because you need to pass the variable to the partial.
the problem is, that your #enquiry variable is not defined in the context you are rendering the partial.
its not defined by the controller action that gets called, you should create a instance of Enquiry by calling
#enquiry = Enquiry.new
in your action.
In Addition
to use it somewhere else i would pass the #enquiry instance variable as a locale variable to the partial
<% render 'enquiries/form', :enquiry => #enquiry %>
your form method should then look like this:
<%= form_for enquiry do |f| %>
...
<% end %>
of course all the instances vars should be replaced then. just remove the '#'
EDIT:
According to your controller setup you posted above the best way would be to use something like
#enquiry ||= Enquiry.new
in your form partial to make shure a new instance is created if #enquiry is nil.

Resources