Nil foreign key in a nested form - ruby-on-rails

I have a nested form with the following models:
class Incident < ActiveRecord::Base
has_many :incident_notes
belongs_to :customer
belongs_to :user
has_one :incident_status
accepts_nested_attributes_for :incident_notes, :allow_destroy => false
end
class IncidentNote < ActiveRecord::Base
belongs_to :incident
belongs_to :user
end
Here is the controller for creating a new Incident.
def new
#incident = Incident.new
#users = #customer.users
#statuses = IncidentStatus.find(:all)
#incident.incident_notes.build(:user_id => current_user.id)
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #incident }
end
end
def create
#incident = #customer.incidents.build(params[:incident])
#incident.incident_notes.build(:user_id => current_user.id)
respond_to do |format|
if #incident.save
flash[:notice] = 'Incident was successfully created.'
format.html { redirect_to(#incident) }
format.xml { render :xml => #incident, :status => :created, :location => #incident }
else
format.html { render :action => "new" }
format.xml { render :xml => #incident.errors, :status => :unprocessable_entity }
end
end
end
This all exists in a nested form for an incident. There is a text area for the incident_notes form that is nested in the incident.
So my problem is that the incident_notes entry is submitted twice whenever I create the incident. The first insert statement creates an incident_note entry with the text from the text area, but it doesn't attach the user_id of the user as the foreign key. The second entry does not contain the text, but it has the user_id.
I thought I could do this with:
#incident.incident_notes.build(:user_id => current_user.id)
but that does not appear to work the way I want. How can I attach the user_id to incident_note?
Thanks!

I finally figured it out. I needed to do this in the Incident controller:
def create
#incident = #customer.incidents.build(params[:incident])
#incident.incident_notes.first.user = current_user
rather than:
def create
#incident = #customer.incidents.build(params[:incident])
#incident.incident_notes.build(:user_id => current_user.id)

I don't think you need
#incident.incident_notes.build(:user_id => current_user.id)
on new action. You're building the incident_notes twice.

Related

NoMethodError in ParticipantsController#new

Why does the error occur?
There hasen't been an adquate/similar solution for my problem. I just could find some tips and tricks here but now I am stuck.
We have a course management system. You can add new coures, participants and persons among other things. I had to change the database. Now there is a persons table, too. Earlier all informations about persons respectively participants where just saved in the participants table. Now when a new participant is added the persons table is involved.
We want to add a new participant of a course. I adjusted the new action in the participants controller and I was hoping passing all data like in the old way. The old way was working to add a new participant.
Earlier the way was: course > new participant form
Now it is: course > search for a person to use it in the form > new participant form
I think (better ways accepted) I just adjust the old code?! Below is my try.
The Error
NoMethodError in ParticipantsController#new undefined method `participants' for []:Array
occurs.
Here are the old classes:
Model Course
class Course < ActiveRecord::Base
has_many :participants
Model Participant
class Participant < ActiveRecord::Base
belongs_to :course
belongs_to :organization
belongs_to :function
ParticipantsController
class ParticipantsController < ApplicationController
....
def new
#course = Course.find(params[:course_id])
#participant = #course.participants.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #participant }
end
end
def create
#course = Course.find(params[:course_id])
#participant = #course.participants.new(params[:participant])
#course.updated_by = current_user.cn
#course.send(:create_version)
#course.tag_version(t(:participant_added))
#course.save!
respond_to do |format|
if #participant.save
format.html { redirect_to course_path(#participant.course), notice: 'Participant was successfully created.' }
format.json { render json: #participant, status: :created, location: #participant }
else
format.html { render action: "new" }
format.json { render json: #participant.errors, status: :unprocessable_entity }
end
end
end
When you look below at the course view snippet there is the old and new path to the form. Note that the person search is in between the course and the new participant form now.
**old** <%= link_to t(:add), new_course_participant_path(#course) %>
**new** <%= link_to t(:add), course_persons_path(#course, #person)%>
Here are the new classes
class Participant < ActiveRecord::Base
belongs_to :course
belongs_to :function
belongs_to :person
class Person < ActiveRecord::Base
has_many :participants
has_many :courses, through: :participants
Here are my adjustments in the ParticipantsController. My thoughts are maybe naive because I am still learning ruby on rails.
class ParticipantsController < ApplicationController
def new
#person = Person.find(params[:person_id])
#participant = Participant.find_by_person_id(params[:person_id])
#course= Course.find(:all, :conditions => {:id => #participant})
#participant = #course.participants.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #participant }
end
end
def create
#course= Course.find(params[:course_id])
#participant = #course.participants.new(params[:participant])
#course.updated_by = current_user.cn
#course.send(:create_version)
#course.tag_version(t(:participant_added))
#course.save!
respond_to do |format|
if #participant.save
format.html { redirect_to course_path(#participant.course), notice: 'Participant was successfully created.' }
format.json { render json: #participant, status: :created, location: #participant }
else
format.html { render action: "new" }
format.json { render json: #participant.errors, status: :unprocessable_entity }
end
end
end
Thanks in advance
Rewrite:
#person = Person.find(params[:person_id])
#participant = Participant.find_by_person_id(params[:person_id])
#course= Course.find(:all, :conditions => {:id => #participant})
#participant = #course.participants.build
To:
#person = Person.find(params[:person_id])
#course = Participant.find_by_person_id(params[:person_id]).course
#participant = #course.participants.build
Watch for out for exception in case Participant.find_by_person_id(params[:person_id]) returns a nil

Matching up collection_select output with strong parameters

In rails console & using the models below, I connected grades K, 1, and 2 to the school whose Edit form has this select field:
As you can see, that association correctly selects the 3 items in the field, but if I click to select/deselect grades, those changes aren't getting saved.
Here are the models:
# app/models/school.rb
class School < ActiveRecord::Base
has_many :grades_schools, inverse_of: :school
has_many :grades, through: :grades_schools
accepts_nested_attributes_for :grades_schools, allow_destroy: true
end
# app/models/grades_school.rb
class GradesSchool < ActiveRecord::Base
belongs_to :school
belongs_to :grade
end
# app/models/grade.rb
class Grade < ActiveRecord::Base
has_many :grades_schools, inverse_of: :grade
has_many :schools, through: :grades_schools
end
The form looks like this:
# app/views/schools/_form.html.haml
= form_for(#school) do |f|
/ <snip> other fields
= collection_select(:school, :grade_ids, #all_grades, :id, :name, {:selected => #school.grade_ids, include_hidden: false}, {:multiple => true})
/ <snip> other fields + submit button
And the controller looks like this:
# app/controllers/schools_controller.rb
class SchoolsController < ApplicationController
before_action :set_school, only: [:show, :edit, :update]
def index
#schools = School.all
end
def show
end
def new
#school = School.new
#all_grades = Grade.all
#grades_schools = #school.grades_schools.build
end
def edit
#all_grades = Grade.all
#grades_schools = #school.grades_schools.build
end
def create
#school = School.new(school_params)
respond_to do |format|
if #school.save
format.html { redirect_to #school, notice: 'School was successfully created.' }
format.json { render :show, status: :created, location: #school }
else
format.html { render :new }
format.json { render json: #school.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #school.update(school_params)
format.html { redirect_to #school, notice: 'School was successfully updated.' }
format.json { render :show, status: :ok, location: #school }
else
format.html { render :edit }
format.json { render json: #school.errors, status: :unprocessable_entity }
end
end
end
private
def set_school
#school = School.find(params[:id])
end
def school_params
params.require(:school).permit(:name, :date, :school_id, grades_attributes: [:id])
end
end
I have a feeling that the crux of my problem has to do with a mismatch between the params generated by collection_select and the strong parameters. One or both of these is probably incorrect, but I can't for the life of me find example code online that shows me what I'm doing wrong.
After trying a load of failed variations, I'm at my wits end! Thanks in advance for your help!
Crap. I could have sworn I tried this before, but it must have been when using fields_for in the form instead of collection_select. The solution:
def school_params
params.require(:school).permit(:name, :date, :school_id, grades_attributes: [:id])
end
becomes
def school_params
params.require(:school).permit(:name, :date, :school_id, grade_ids: [])
end
I'm still curious how it would work when using fields_for #grades_schools, but will have to save that investigation for another day....

Adding to has_one if text_field != blank or nil

I have a simple has_one relationship setup but I want to add records to Damages table only when the text_field is not blank or nil. Right now it's adding records that are blank or nil to the other table.
My view:
<%= f.fields_for :damage do |builder| %>
<%= builder.label 'Damage' %><br />
<%= builder.text_field :dam_detail %>
<% end %>
Packjob Model:
class Packjob < ActiveRecord::Base
attr_accessible :pj_damage
has_one :damage
accepts_nested_attributes_for :damage
end
Damage Model:
class Damage < ActiveRecord::Base
attr_accessible :dam_detail
belongs_to :packjob
end
How do I allow only non blank or nil values being added?
Is best practice to add logic for this to the helper?
EDIT:
Here's the controller for Packjobs:
class PackjobsController < ApplicationController
# GET /packjobs
# GET /packjobs.json
def index
#packjobs = Packjob.includes(:damage).all
#packers = Packer.find(:all)
#rigs = Rig.find(:all, :order => "rig_type_number")
respond_to do |format|
format.html # index.html.erb
format.json { render json: #packjobs }
end
end
# GET /packjobs/1
# GET /packjobs/1.json
def show
#packjob = Packjob.find(params[:id])
#packers = Packer.find(:all)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #packjob }
end
end
# GET /packjobs/new
# GET /packjobs/new.json
def new
#packjob = Packjob.new
#packers = Packer.find(:all, :conditions => { :p_team => "t" }, :order => "p_name")
#rigs = Rig.find(:all, :conditions => { :rig_status => "t" }, :order => "rig_type_number")
#damage = #packjob.build_damage
##book = #author.build_book
respond_to do |format|
format.html # new.html.erb
format.json { render json: #packjob }
end
end
# GET /packjobs/1/edit
def edit
#packjob = Packjob.find(params[:id])
#packers = Packer.find(:all, :conditions => { :p_team => "t" }, :order => "p_name")
#rigs = Rig.find(:all, :conditions => { :rig_status => "t" }, :order => "rig_type_number")
end
# POST /packjobs
# POST /packjobs.json
def create
#packjob = Packjob.new(params[:packjob])
#packers = Packer.find(:all, :conditions => { :p_team => "t" }, :order => "p_name")
#rigs = Rig.find(:all, :conditions => { :rig_status => "t" }, :order => "rig_type_number")
respond_to do |format|
if #packjob.save
format.html { redirect_to #packjob, notice: 'Packjob was successfully created.' }
format.json { render json: #packjob, status: :created, location: #packjob }
else
format.html { render action: "new" }
format.json { render json: #packjob.errors, status: :unprocessable_entity }
end
end
end
# PUT /packjobs/1
# PUT /packjobs/1.json
def update
#packjob = Packjob.find(params[:id])
#packers = Packer.find(:all, :conditions => { :p_team => "t" }, :order => "p_name")
#rigs = Rig.find(:all, :conditions => { :rig_status => "t" }, :order => "rig_type_number")
respond_to do |format|
if #packjob.update_attributes(params[:packjob])
format.html { redirect_to #packjob, notice: 'Packjob was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #packjob.errors, status: :unprocessable_entity }
end
end
end
# DELETE /packjobs/1
# DELETE /packjobs/1.json
def destroy
#packjob = Packjob.find(params[:id])
#packjob.destroy
respond_to do |format|
format.html { redirect_to packjobs_url }
format.json { head :no_content }
end
end
end
Also the I want the packjob to allow for blanks in damage text_field, I just don't want the records added to the Damages table..
This is a job for validations. Specifically, you want two things:
You want your Damage class to validate that :dam_detail is not blank or nil:
class Damage < ActiveRecord::Base
# ... rest of class here ...
validates :dam_detail, :presence => true, :length => { :minimum => 1 }
end
You want your Packjob class to validate that its contained Damage object is valid:
class Packjob < ActiveRecord::Base
# ... rest of class here ...
validates_associated :damage
end
I also recommend modifying your database schema to add the restriction that the dam_detail field cannot be null. See the migrations guide for more info.
Even though its more commonly used for has_many the cocoon gem https://github.com/nathanvda/cocoon is great for this. That gem will allow you to build the relation on the fly from the front end. it will also allow you to destroy the relation too.

Rails: model accepts nested attributes but controller doesn't seem to care

I'm struggling to get nested attributes down. Working off of Railscast 196, I tried to setup my own app that does basic nesting. Users can create scavenger hunts. Each hunt consists of a series of tasks (that can belong to any hunt, not just one). I got a little help here and tried to learn from a post with a similar issue, but I'm still stuck. I've been hacking around for hours and I've hit a brick wall.
class HuntsController < ApplicationController
def index
#title = "All Hunts"
#hunts = Hunt.paginate(:page => params[:page])
end
def show
#hunt = Hunt.find(params[:id])
#title = #hunt.name
#tasks = #hunst.tasks.paginate(:page => params[:page])
end
def new
if current_user?(nil) then
redirect_to signin_path
else
#hunt = Hunt.new
#title = "New Hunt"
3.times do
#hunt = #hunt.tasks.build
#hunt = #hunt.hunt_tasks.build
hunt = #hunt.hunt_tasks.build.build_task
end
end
end
def create
#hunt = Hunt.new(params[:hunt])
if #hunt.save
flash[:success] = "Hunt created!"
redirect_to hunts_path
else
#title = "New Hunt"
render 'new'
end
end
....
end
With this code, when I try and create a new hunt, I'm told that there's no method "build_task" (it's undefined). So when I remove that line and use the second bit of code that's commented out above, I get the error below.
NoMethodError in Hunts#new
Showing /Users/bendowney/Sites/MyChi/app/views/shared/_error_messages.html.erb where line #1 raised:
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.errors
Extracted source (around line #1):
1: <% if object.errors.any? %>
2: <div id="error_explanation">
3: <h2><%= pluralize(object.errors.count, "error") %>
4: prohibited this <%= object.class.to_s.underscore.humanize.downcase %>
Trace of template inclusion: app/views/tasks/_fields.html.erb, app/views/hunts/_fields.html.erb, app/views/hunts/new.html.erb
And when I use the first bit of code that's commented out in the hunt controller, then I get an error telling me that my 'new' method has an unintialized constant:
NameError in HuntsController#new
uninitialized constant Hunt::Tasks
I'm at my wit's end. Any suggestions on what exactly I'm doing wrong? Or a strategy Here are my models:
class Hunt < ActiveRecord::Base
has_many :hunt_tasks
has_many :tasks, :through => :hunt_tasks #, :foreign_key => :hunt_id
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class Task < ActiveRecord::Base
has_many :hunt_tasks
has_many :hunts, :through => :hunt_tasks#, :foreign_key => :hunt_id
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class HuntTask < ActiveRecord::Base
belongs_to :hunts # the id for the association is in this table
belongs_to :tasks
end
When you create an association between 2 of your models, you add functionality to them, depending on how you define your relationship. Each type kinda adds different functions to your model.
I really recommend reading this guide -> http://guides.rubyonrails.org/association_basics.html
Here you can see which functions get added by each different type of association.
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
If I do a small sample program like...
class HuntsController < ApplicationController
# GET /hunts
# GET /hunts.json
def index
#hunts = Hunt.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #hunts }
end
end
# GET /hunts/1
# GET /hunts/1.json
def show
#hunt = Hunt.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #hunt }
end
end
# GET /hunts/new
# GET /hunts/new.json
def new
#hunt = Hunt.new
3.times do |i|
t = #hunt.hunt_tasks.build
t.name = "task-#{i}"
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: #hunt }
end
end
# GET /hunts/1/edit
def edit
#hunt = Hunt.find(params[:id])
end
# POST /hunts
# POST /hunts.json
def create
#hunt = Hunt.new(params[:hunt])
respond_to do |format|
if #hunt.save
format.html { redirect_to #hunt, notice: 'Hunt was successfully created.' }
format.json { render json: #hunt, status: :created, location: #hunt }
else
format.html { render action: "new" }
format.json { render json: #hunt.errors, status: :unprocessable_entity }
end
end
end
# PUT /hunts/1
# PUT /hunts/1.json
def update
#hunt = Hunt.find(params[:id])
respond_to do |format|
if #hunt.update_attributes(params[:hunt])
format.html { redirect_to #hunt, notice: 'Hunt was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #hunt.errors, status: :unprocessable_entity }
end
end
end
# DELETE /hunts/1
# DELETE /hunts/1.json
def destroy
#hunt = Hunt.find(params[:id])
#hunt.destroy
respond_to do |format|
format.html { redirect_to hunts_url }
format.json { head :no_content }
end
end
end
and this model-relation
class Hunt < ActiveRecord::Base
has_many :hunt_tasks
end
class HuntTask < ActiveRecord::Base
belongs_to :hunt
end
and add this snippet somewhere in views/hunts/_form.html
<% #hunt.hunt_tasks.each do |t| %>
<li><%= t.name %></li>
<% end %>
I get regular output, seeing that the 3 tasks were created.
have you tried
hunttask = #hunt.build_hunt_task
in the HuntsController new action?
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
The immediate error you are seeing is in app/views/shared/_error_messages.html.erb. object is not defined, You probably need to find where that partial is called. Find:
render :partial=>"/shared/error"
replace it with
render :partial=>"/shared/error", :locals=>{:object=>#hunt}
If you find it in app/views/hunts somewhere, if you find in in app/views/tasks, replace #hunt with #task
That will at least show you what the real error is.

When submitting a form with a fields_for part, how can I assign the new id to the fields_for model

I have a form that handles 2 models, Vehiculo and Poliza. This is how I have them set up right now:
class Vehiculo < ActiveRecord::Base
has_one :poliza
end
class Poliza < ActiveRecord::Base
belongs_to :vehiculo
end
The create method on Vehiculo looks like this:
def create
#vehiculo = Vehiculo.new(params[:vehiculo])
#polizadeseguro = Polizadeseguro.new(params[:poliza])
respond_to do |format|
if #vehiculo.save #&& #poliza.save
format.html { redirect_to(#vehiculo, :notice => 'Vehiculo was successfully created.') }
format.xml { render :xml => #vehiculo, :status => :created, :location => #vehiculo }
else
format.html { render :action => "new" }
format.xml { render :xml => #vehiculo.errors, :status => :unprocessable_entity }
end
end
The form on /vehiculos/new has a #fields_for part with the fields from poliza. When I submit the form, it is saving all the fields, but it is not assigning the just created id from vehiculo, to vehiculo_id on the Polizas table. After reading many questions about this online, It seems that it should save it "automagically" based on the relationships on the model. Is this true? If so, why isn't it working? If not, what do I need to add to the create method so I resolve this?
Thanks!
Update:
After updating the create method with json as output as suggested here is what I get:
{
"utf8"=>"✓",
"authenticity_token"=>"tEhNC4J17h+KvNgXv1LLkVyufQwU2uAT18P7msQxiqA=",
"vehiculo"=>{
"marca_id"=>"2",
"modelo_id"=>"4",
"color"=>"Blanco",
"ano"=>"2011",
"chassis"=>"123456789",
"placa"=>"G123456",
"cliente_id"=>"1",
"entaller"=>"0",
"vip"=>"0"
},
"poliza"=>{
"compania"=>"Comp1",
"numeropoliza"=>"736458",
"vencimiento(1i)"=>"2011",
"vencimiento(2i)"=>"9",
"vencimiento(3i)"=>"21"
}
}
That's the output, so at least it is getting the fields from the form, but it is not inserting them to the polizas table.
You need to make sure that your parent model accepts nested attributes for the child model:
class Vehiculo < ActiveRecord::Base
has_one :poliza
accepts_nested_attributes_for :poliza
end
Assuming your form is set up correctly, your params will look something like this:
params = {
:vehiculo => {
:field => "value",
:another_field => "value",
:poliza => {
:poliza_field => "poliza value"
}
}
}
So all you should need in your controller is:
def create
#vehiculo = Vehiculo.new(params[:vehiculo])
respond_to do |format|
if #vehiculo.save #&& #poliza.save
format.html { redirect_to(#vehiculo, :notice => 'Vehiculo was successfully created.') }
format.xml { render :xml => #vehiculo, :status => :created, :location => #vehiculo }
else
format.html { render :action => "new" }
format.xml { render :xml => #vehiculo.errors, :status => :unprocessable_entity }
end
end
end
[Update]
Here's what you'll need to have to have this all work.
As mentioned above, you need accepts_nested_attributes_for.
Next, make sure your new action is building the child.
class VehiculosController < ApplicationController
def new
#vehiculo = Vehiculo.new
#vehiculo.build_poliza
end
def create
vehiculo = Vehiculo.new(params[:vehiculo])
if vehiculo.save
redirect_to root_path, :notice => "Success"
else
redirect_to root_path, :alert => "Failure"
end
end
end
Finally, in your view, reference the child model using fields_for :child_model, as such:
<%= form_for #vehiculo do |f| %>
<p>Whatever Field: <%= f.text_field :whatever %></p>
<%= f.fields_for :poliza do |p| %>
<p>Polizo Field: <%= p.text_field :something %></p>
<% end %>
<% end %>

Resources