Creating associated object from collection select - ruby-on-rails

I have multiple Lessons and most of them have some prerequisites which are themselves other lessons. The Prerequisite model represents the relationship and each has a lesson_id and prerequisite_lesson_id.
class Lesson < ApplicationRecord
has_many :prerequisites
has_many :prerequisite_lessons, :through => :prerequisites
end
class Prerequisite < ApplicationRecord
belongs_to :lesson
belongs_to :prerequisite_lesson, :class_name => "Lesson"
end
I'm trying to figure out a way so that:
When I create a new Lesson and select one or more prerequisites from a collection_select
multiple Prerequisite objects are created based on what was selected, with the lesson_id being the created Lesson id.
Here is part of my Lesson controller:
def create
#lesson = Lesson.new(lesson_params)
if #lesson.save
if #lesson.prerequisite_ids.length > 0
#lesson.prerequisite_ids.each do |p|
Prerequisite.new(lesson_id: #lesson.id, prerequisite_lesson_id: p)
end
end
flash[:notice] = "Lesson created..."
redirect_to root_url
else
render :new
end
end
...
def lesson_params
params.require(:lesson).permit(
:name,
:high_tempo,
:low_tempo,
:interval,
:advance_tempo,
prerequisites_attributes: [
:lesson_id,
:prerequisite_lesson_id
]
)
end
and my form:
<%= f.collection_select(:prerequisite_ids, Lesson.all, :id, :name, {}, {:multiple => true}) %>
UPDATE:
In the logs, I see:
"lesson"=>{"name"=>"Lesson 6", "high_tempo"=>"200",
"low_tempo"=>"100", "interval"=>"10", "advance_tempo"=>"140",
"prerequisite_ids"=>["", "3", "4"]}, "commit"=>"Save"} Unpermitted
parameter: prerequisite_ids'`

It looks like there are a couple of problems here:
One is that you are using Prerequisite.new(...) when I think you want to be using Prerequisite.create(...)
So your create action should be:
def create
#lesson = Lesson.new(lesson_params)
if #lesson.save
if #lesson.prerequisite_ids.length > 0
#lesson.prerequisite_ids.each do |p|
Prerequisite.create(lesson_id: #lesson.id, prerequisite_lesson_id: p)
end
end
flash[:notice] = "Lesson created..."
redirect_to root_url
else
render :new
end
end
The other is this (from your logs):
"lesson"=>{"name"=>"Lesson 6", "high_tempo"=>"200",
"low_tempo"=>"100", "interval"=>"10", "advance_tempo"=>"140",
"prerequisite_ids"=>["", "3", "4"]}, "commit"=>"Save"} Unpermitted
parameter: prerequisite_ids'`
In your controller, you are permitting
def lesson_params
params.require(:lesson).permit(
:name,
:high_tempo,
:low_tempo,
:interval,
:advance_tempo,
prerequisites_attributes: [
:lesson_id,
:prerequisite_lesson_id
]
)
end
But note that your actual data is coming in as "lesson" => {..., "prerequisite_ids"=>["", "3", "4"]}
So you should instead permit like this:
def lesson_params
params.require(:lesson).permit(
:name,
:high_tempo,
:low_tempo,
:interval,
:advance_tempo,
prerequisite_ids: []
)
end
Additionally, it looks like your prerequisite_ids are including a value that is an empty string. Based on this post, it looks like you may need to add include_hidden: true to your select tag, like this:
<%= f.collection_select(:prerequisite_ids, Lesson.all, :id, :name, {}, {:multiple => true, include_hidden: true}) %>

Related

Select field to create or update has_many records on update

I want to have a simple select field that allows users to choose some users to be on a job's team.
#jobs.rb
class Job < ApplicationRecord
has_many :team_members
end
#jobs_controller.rb
class JobsController < ApplicationController
def update
#job = Job.find(params[:id])
if #job.update_attributes(job_params)
flash[:notice] = "Job Saved"
redirect_to job_path(#job)
else
flash[:alert] = "Job Not Saved"
render :edit
end
end
private
def job_params
params.require(:job).permit(
team_member_ids: []
)
end
end
# jobs/edit.html.erb
<%= f.select :team_member_ids, User.all, {:include_blank => "None"},{ :multiple => true} %>
This gives me an error
Couldn't find TeamMembers with ids 117, 23, 30.
Am I missing something simple here? Or is the wrong approach to managing the data in the form?
Update, params hash:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"xxxx",
"job"=>
{
"jobtype"=>"Cutting",
"status"=>"Not_Started",
"team_member_ids"=>["", "117", "23", "30"]},
"commit"=>"Update Job",
"id"=>"84"}
Oops, This was a bit silly.
The id's are user id's, but I was passing them as team_member ids. Where team_member is the join table.
Fix was:
#add to job.rb
has_many :users, through: :team_members
# update jobs_controller.rb
private
def job_params
params.require(:job).permit(
user_ids: []
)
end
# update form field:
<%= f.select :user_ids, User.all,{:include_blank => "None"},{ :multiple => true} %>

How to merge default values into nested params in Rails 5?

I am desperately trying to merge a set of default values into my nested params. Unfortunately, using deep_merge no longer works in Rails 5 since it no longer inherits from Hash.
So this does not work:
class CompaniesController < ApplicationController
def create
#company = current_account.companies.build(company_params)
if #company.save
flash[:success] = "Company created."
redirect_to companies_path
else
render :new
end
end
private
def company_params
params.require(:company).permit(
:name,
:email,
:people_attributes => [
:first_name,
:last_name
]
).deep_merge(
:creator_id => current_user.id,
:people_attributes => [
:creator_id => current_user.id
]
)
end
end
It gives me this error:
undefined method `deep_merge' for ActionController::Parameters:0x007fa24c39cfb0
So how can it be done?
Since there seems to be no similar implementation of deep_merge in Rails 5 ActionController::Parameters by looking at this docs, then you can just simply do .to_h to convert it first into a ActiveSupport::HashWithIndifferentAccess (which is a subclass of a Hash):
to_h() Returns a safe ActiveSupport::HashWithIndifferentAccess representation of the parameters with all unpermitted keys removed.
def company_params
params.require(:company).permit(
:name,
:email,
:people_attributes => [
:first_name,
:last_name
]
).to_h.deep_merge(
:creator_id => current_user.id,
:people_attributes => [
:creator_id => current_user.id
]
)
end

How can get array values using has_and_belongs_to_many?

Can't save params selected on select box.
Table users:
1id| |name|
1 CR7
2 Messi
Table ejecutives:
1id| |name|
1 Mourinho
2 Guardiola
Table user_ejecutives:
|id| |user_id| |ejecutive_id|
1 1 1
2 2 2
Controller users_controller.rb:
def new
#obj_user = User.new
end
def create
#user = User.new user_params
#user.save
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:name, user_ejecutive_ids: [])
end
Models:
#User.rb
has_many :ejecutives, :through => :user_ejecutives
has_many :user_ejecutives
has_and_belongs_to_many :user_ejecutives, class_name: "User", join_table: "user_ejecutives"#, foreign_key: :user_id, association_foreign_key: :ejecutive_id
#Ejecutive.rb
has_many :user_ejecutives
has_many :users, :through => :user_ejecutives
#UserEjecutive.rb
belongs_to :user
belongs_to :ejecutive
View new.html.erb:
<%= form_for #user do |f| %>
<%= form.text_field :name %>
<%= f.collection_select :user_ejecutive_ids, Ejecutive.all, :id, :name, multiple: true %>
<% end %>
View show.html.erb
<% #user.ejecutives.each do |ejecutive| %>
<%= ejecutive.name %></label>
<% end %>
I'm not getting results on the view show and it show on logs:
SystemStackError (stack level too deep):
If you're just trying to populate the join table (user_ejecutives), you'll want to populate the singular_colletion_ids method:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new user_params
#user.save
end
private
def user_params
params.require(:user).permit(:name, user_ejecutive_ids: [])
end
end
#app/views/users/new.html.erb
<%= form_for #user do |f| %>
<%= f.collection_select :user_ejecutive_ids, User.all, :id, :name, multiple: true %>
<%= f.submit %>
<% end %>
This will assign new user_ejecutives for each new #user you create.
PS User.all is valid in this instance as you're dealing with a new (uncreated) #user record, hence it won't appear in the db.
If you wanted to create new user_ejecutives with each new #user, you'll want to use accepts_nested_attributes_for, which I can explain if required.
Update
So your error is as follows:
Unpermitted parameter: user_ejecutive_ids
... you also have another error...
NoMethodError (undefined method `each' for nil:NilClass):
This is exactly why I don't like your code. Because it doesn't fit to convention, you've go to evaluate whether the params are present etc.
You'll need to use the controller code I posted - it will populate the other table for you, and fix this NilClass error.
--
Join Table
Your user_ejecutives table is a join table.
Your User model should have the following:
#app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :user_ejecutives, class_name: "User", join_table: "user_ejecutives", foreign_key: :user_id, association_foreign_key: :ejecutive_id
end
You'll have to remove the id column from your user_ejecutives table (as per the definition here). The importance of this is that it gives you the ability to populate the singular_collection_ids method (in your case user_ejective_ids), as per my recommended code.
Try the following code.
params.require(:user).permit(:name, :user_ejecutives => [])
Hey, I think you have "ejecutive_id" column declared as integer but when loop through "user_ejecutives" you are getting each value as string, May be this is causing the issue, Kindly update your create action to below.
def create
obj_user = User.new(user_params)
if obj_user.save
params[:user_ejecutives].each do |ejecutive|
user_ejecutive = UserEjecutive.create(user_id: obj_user.id, ejecutive_id: ejecutive.to_i)
user_ejecutive.save
end
end
end

Ruby on Rails : Updating multiple models in a single form

I have 3 models User, House and Order.
Order Model
class Order < ActiveRecord::Base
belongs_to :from_house, :class_name => "House"
belongs_to :to_house, :class_name => "House"
belongs_to :user
accepts_nested_attributes_for :from_house, :to_house, :user
end
My House Model.
class House < ActiveRecord::Base
belongs_to :user
belongs_to :place
belongs_to :city
end
My user model.
class User < ActiveRecord::Base
has_many :orders
has_many :houses
end
In my order form I have something like this
<%= form_for #order do |f| %>
... # order fields
<%= f.fields_for :user do |i| %>
... # your from user forms
<% end %>
<%= f.fields_for :from_house do |i| %>
... # your from house forms
<% end %>
<%= f.fields_for :to_house do |i| %>
... # your to house forms
<% end %>
...
<% end %>
I haven't changed much in controller from the default. The controller code
def create
#order = Order.new(order_params)
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render action: 'show', status: :created, location: #order }
else
format.html { render action: 'new' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
def order_params
params.require(:order).permit( :shift_date, user_attributes: [:name, :email, :ph_no], from_house_attributes: [:place_id, :floor, :elevator, :size], to_house_attributes: [:place_id, :floor, :elevator])
end
When I submit the form, as expected a Order gets created with a new from_house and to_house along with a new user. But however my user_id in house table remains NULL. How can I make the houses(both from and to) reference the user created after submit.
The User is not logged in, So there is no current_user. We have to create a new user based on the details given. That user has to be associated with the houses (from and to).
I hope I'm clear. If not please let me know.
P.S: This question is an extension to this Ruby on rails: Adding 2 references of a single model to another model
I think this change in app/models/order.rb should do the trick:
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :from_house, class_name: 'House'
belongs_to :to_house, class_name: 'House'
accepts_nested_attributes_for :user, :from_house, :to_house
validates :user, :from_house, :to_house, presence: true
def from_house_attributes=(attributes)
fh = build_from_house(attributes)
fh.user = self.user
end
def to_house_attributes=(attributes)
th = build_to_house(attributes)
th.user = self.user
end
end
Now, try this in your Rails console:
params = { user_attributes: { name: 'New name', email: 'name#example.com' }, from_house_attributes: { name: 'From house name' }, to_house_attributes: { name: 'to house name' } }
o = Order.new(params)
o.save
o.from_house
o.to_house
Cheers!

getting null value in my table?

I am creating employee pay roll application. In that i am having two models one is employee and another is salary. I have added foreign key to get the employee_id in salaries table and to get the employee name in _form.html.erb(salaries) using the gem 'rails3-jquery-autocomplete'. I have followed all the steps in that gem. But foreign key relationship is not working. I am getting null value in the field employee_id in salaries table.
'
Here i have attached my model,view,controller,routes and everything. Kindly check it.
My Models
** Salary.rb**
class Salary < ActiveRecord::Base
belongs_to :employee, :foreign_key => "employee_id"
attr_accessible :basic, :da, :effective_from, :effective_to, :employeesalary, :hra, :ca, :sa, :employee_id
end
Employee.rb
class Employee < ActiveRecord::Base
has_many :salaries, :foreign_key => "employee_id"
attr_accessible :address, :age, :branch, :city, :date_of_birth, :designation, :employee_end_date, :employee_start_date, :gender, :name
end
My Controllers
salaries_controller.rb
class SalariesController < ApplicationController
autocomplete :employee, :name, :display_value => :name, :full => true
def new
#salary = Salary.new
end
def create
#salary = Salary.new(params[:salary])
if #salary.save
flash[:success] = "Created a new month salary!"
redirect_to salaries_path
else
flash[:error] = "Couldn't Create salary"
render 'new'
end
end
def index
#salary = Salary.order('created_at DESC').paginate(:page => params[:page], :per_page => 10)
end
def show
#salary = Salary.find(params[:id])
#employee =Employee.all
end
end
employees_controller.rb
class EmployeesController < ApplicationController
def new
#employee = Employee.new
#salary = #employee.salaries.build
end
def create
#employee = Employee.new(params[:employee])
if #employee.save
flash[:success] = "Created Employee Successfully!"
redirect_to employee_path(#employee)
else
flash[:error] = "Couldn't Create a Employee"
render 'new'
end
end
def index
#employee = Employee.order('created_at DESC').paginate(:page => params[:page], :per_page => 10)
end
def show
#employee = Employee.find(params[:id])
end
end
routes.rb
resources :salaries do
get :autocomplete_employee_name, :on => :collection
end
resources :employees
_form.html.erb(salaries)
<%= form_for #salary do |f| %>
<%= f.hidden_field :employee_id, :id => "real_employee_id" %>
<span class="help-block">Enter the Employee name</span>
<%= autocomplete_field_tag 'throwaway_employee', '',
autocomplete_employee_name_salaries_path,
:size => 75, :id_element => '#real_employee_id' %>
application.js
//= require jquery
//= require jquery_ujs
//= require autocomplete-rails
//= require_tree .
I have checked throughly but can't find the error. Kindly help me to get out of this issue.
Thanks in advance...
I had a play with this code tonight and I think I know what might be happening.
When you start typing in an employee name a green list of names pops up. If you just hit enter straight away the form takes whatever you have typed so far and uses it to look up the name. This fails because the name is incomplete. As you have no validation on the employee id the form works and a null employee id is saved.
What you need to do is to use the down arrow key or mouse to select one of the names. When you do that the name will change to light green and the text in the text box will be replaced with the full name.
Then when you submit the form the match is found and everything works!
So your code works, you are just using the GUI incorrectly.
You should also add this line to your salary.rb file:
validates :employee_id, presence: true
That's my theory. Please try it out and let us know how you go.

Resources