Prevent creating blank associations in nested attributes forms - ruby-on-rails

I have a form with nested attributes that is sending to the Appointments controller which is creating instances of associated models called Prescriptions, Illnesses, Allergies, and Immunizations. The idea is that a doctor can fill out the form and can add information where needed, but isn't required to fill out everything (not every appointment requires a prescription).
Currently, the form sends out and creates new instances of all the associations with all the attributes blank or nil. I tried adding a validation requiring the presence of the attributes to save, but that creates an error and then nothing can save.
How can I submit one form and prevent it from creating instances of associated models if the fields for that model were empty?
Appointments Controller
def create
#appointment = current_user.appointments.new(appointment_params)
set_associations(#appointment, #patient)
if #appointment.save
Notification.create_appointment_notifications(#appointment, #health_profile)
flash[:notice] = "Your appointment has been saved."
redirect_to patient_health_profile_path(#patient, #health_profile)
else
flash[:notice] = "There was a problem saving your appointment, please try again."
redirect_to patient_health_profile_path(#patient, #health_profile)
end
end
private
def appointment_params
symptoms = params[:appointment][:symptoms].split(",")
#patient = Patient.find(params[:patient_id])
params.require(:appointment).permit(
:diagnosis, :referrals, :notes, :symptoms,
immunizations_attributes: [:name, :expiration_date],
illnesses_attributes: [:name, :status],
allergies_attributes: [:name, :status, :severity],
prescriptions_attributes: [:medicine, :dosage, :refills, :expiration_date]
).merge(symptoms: symptoms,
patient: #patient
)
end
def set_associations(appointment, patient)
appointment.illnesses.each do |illness|
illness.patient = patient
end
appointment.allergies.each do |allergy|
allergy.patient = patient
end
appointment.immunizations.each do |immunization|
immunization.patient = patient
end
appointment.prescriptions.each do |prescription|
prescription.patient = patient
prescription.doctor = current_user
end
end
def set_information
#patient = Patient.find(params[:patient_id])
#health_profile = HealthProfile.find(params[:id])
end
Nested Form
<%= form_for #appointment do |f| %>
<div>
<h4>Appointment</h4>
<div>
<%= f.label :Symptoms %>
<%= f.text_field :symptoms, id: "symptoms-input" %>
</div>
<div>
<%= f.label :Notes %>
<%= f.text_area :notes %>
</div>
<div>
<%= f.label :Diagnosis %>
<%= f.text_field :diagnosis %>
</div>
<div>
<%= f.label :Referrals %>
<%= f.text_area :referrals, id: "referrals-input" %>
</div>
</div>
<div>
<h4>Illnesses</h4>
<div>
<%= f.fields_for :illnesses do |illness| %>
<%= render "appointments/illness_fields", f: illness %>
<% end %>
</div>
<div>
<%= link_to_add_association '+ Illness', f, :illnesses, partial: "appointments/illness_fields" %>
</div>
</div>
<br>
<div>
<h4>Allergies</h4>
<div>
<%= f.fields_for :allergies do |allergy| %>
<%= render "appointments/allergy_fields", f: allergy %>
<% end %>
</div>
<div>
<%= link_to_add_association '+ Allergy', f, :allergies, partial: "appointments/allergy_fields" %>
</div>
</div>
<br>
<div>
<h4>Immunizations</h4>
<div>
<div>
<%= f.fields_for :immunizations do |immunization| %>
<%= render "appointments/immunization_fields", f: immunization %>
<% end%>
</div>
<div>
<%= link_to_add_association '+ Immunization', f, :immunizations, partial: "appointments/immunization_fields" %>
</div>
</div>
</div>
<br>
<div>
<h4>Prescriptions</h4>
<div>
<%= f.fields_for :prescriptions do |prescription| %>
<%= render "appointments/prescription_fields", f: prescription %>
<% end %>
</div>
<div>
<%= link_to_add_association '+ Prescription', f, :prescriptions, partial: "appointments/prescription_fields" %>
</div>
</div>
<%= hidden_field_tag(:patient_id, params[:patient_id]) %>
<%= hidden_field_tag(:id, params[:id]) %>
<div>
<%= f.submit :Submit %>
</div>
<% end %>

In your Appointments Model, add this line:
accepts_nested_attributes_for :illnesses, reject_if: :all_blank, allow_destroy: true

Related

Rails - create and update action don't work

I'm a beginner in Rails, I have a Suplement controller and I can't create or edit a suplement (delete works fine). I'm not getting any errors, just nothing happens when I click and everything's working fine from the console. I tried everything I know (which is not much) and I couldn't find a question like this, similar answers didn't help. I'd appreciate any help, thanks!
class SuplementsController < ApplicationController
before_action :set_suplement, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#suplement = Suplement.all.order("created_at DESC")
end
def new
#suplement = Suplement.new
end
def create
#suplement = Suplement.new(suplement_params)
if #suplement.save
redirect_to '/suplements'
else
render '/suplements/new'
end
end
def show
end
def edit
end
def update
if #suplement.update(suplement_params)
redirect_to '/suplements'
else
redirect_to '/suplements/new'
end
end
def destroy
#suplement.destroy
redirect_to '/suplements'
end
private
def set_suplement
#suplement = Suplement.find(params[:id])
end
def suplement_params
params.require(:suplement).permit(:name,
:number_of_units,
:daily_dosage_in_units,
:number_of_days,
:suplement_cost
)
end
end
Here's a view:
<h1>Create new suplement</h1>
<%= form_for(#suplement) do |f| %>
<%= render 'form', suplement: #suplement %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
and here's a form partial:
<%= form_for(#suplement) do |f| %>
<% if #suplement.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#suplement.errors.count, "error") %> prohibited this suplement from being saved:</h2>
<ul>
<% #suplement.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :number_of_units %>
<%= f.text_field :number_of_units %>
</div>
<div class="field">
<%= f.label :daily_dosage_in_units %>
<%= f.text_area :daily_dosage_in_units %>
</div>
<div class="field">
<%= f.label :number_of_days %>
<%= f.text_area :number_of_days %>
</div>
<div class="field">
<%= f.label :suplement_cost %>
<%= f.text_area :suplement_cost %>
</div>
<% end %>
Also my models:
class Suplement < ApplicationRecord
belongs_to :user
validates :name,
:number_of_units,
:daily_dosage_in_units,
:number_of_days,
:suplement_cost,
presence: true
end
and
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :suplements
end
It looks like the problem is that you have 2 forms.
Uou have a form_for #suplement in your _form.html.erb file and also in your new.html.erb file. Try removing it from new.html.erb so your file looks like this
new.html.erb
<h1>Create new suplement</h1>
<%= render 'form', suplement: #suplement %>
_form.html.erb
<%= form_for(#suplement) do |f| %>
<% if #suplement.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#suplement.errors.count, "error") %> prohibited this suplement from being saved:</h2>
<ul>
<% #suplement.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :number_of_units %>
<%= f.text_field :number_of_units %>
</div>
<div class="field">
<%= f.label :daily_dosage_in_units %>
<%= f.text_area :daily_dosage_in_units %>
</div>
<div class="field">
<%= f.label :number_of_days %>
<%= f.text_area :number_of_days %>
</div>
<div class="field">
<%= f.label :suplement_cost %>
<%= f.text_area :suplement_cost %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
What I did is:
1) Deleted form_for and submit button inside new.html.erb
2) Added submit button in _form.html.erb, so the variable f is accessible
Also, since you are passing a variable #suplement to partial local variable suplement, you can use the variable suplement inside _form.html.erb file without the # sign
EDIT (Regarding comment):
Your getting User presence validation Error, because from Rails 5.0, belongs_to associations are automatically validated for presence.
If you do not need a user in your suplement object all the time then you should change your association to belongs_to :user, optional: true
OR
if you do need the user, and you always want it to be the current user logged in, then add this to your _form
<%=f.hidden_field :user_id, current_user.id %>
This will use Devise helper method to get the current logged in user and assign it to this hidden field. Don't forget to add this parameter in your controler suplement_params controller method
In the #edit of your controller, you need to set the value of the #suplement variable.
def edit
#suplement = Suplement.find(params[:id])
end
you should also include the above line as the first line in your #update method
def update
#suplement = Suplement.find(params[:id])
if #suplement.update_attributes(suplement_params)
# continued...
end

Rails - Couldn't find Student with 'id'=

I'm getting the error above when I try to create a a 'mark' for a 'student'. I can't figure out how to pass the :student_id when I create a new mark.
Routes
Rails.application.routes.draw do
resources :students do
resources :marks
end
resources :marks
root 'students#index'
Marks Controller
class MarksController < ApplicationController
def create
#student = Student.find(params[:student_id])
#mark = #student.marks.create(params[:input1, :input2, :input3, :weight1, :weight2, :weight3, :mark1, :mark2, :mark3, :final_mark].permit(:input1, :input2, :input3, :weight1, :weight2, :weight3, :mark1, :mark2, :mark3, :final_mark))
#mark.save
if #mark.save
redirect_to student_path(#student)
else
render 'new'
end
end
def new
#mark = Mark.new
end
private
def set_mark
#mark = Mark.find(params[:id])
end
end
Students Show View
<p id="notice"><%= notice %></p>
<p>
<strong>Student Number</strong>
<%= #student.StudentNumber %>
</p>
<p>
<strong>Project Title</strong>
<%= #student.ProjectTitle %>
</p>
<p>
<strong>Project PDF</strong>
<%= #student.ProjectTitle %>
</p>
<p>
<strong>Reader 1</strong>
<%= #student.Reader1 %>
</p>
<p>
<strong>Reader 2</strong>
<%= #student.Reader2 %>
</p>
<h3> <%= link_to 'Add Mark', new_student_mark_path(#student), class:"btn btn-warning"%> </h3>
<p>
<strong>Reader 3</strong>
<%= #student.Reader3 %>
</p>
<%= link_to 'Edit', edit_student_path(#student) %> |
<%= link_to 'Back', students_path %>
Marks Form
<%= form_for #mark, html: {multipart: true} do |f| %>
<% if #mark.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#mark.errors.count, "error") %> prohibited this grading from being saved:</h2>
<ul>
<% #mark.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label 'Introduction' %><br>
<%= f.text_area :input1 %>
<%= f.number_field :weight1 %>
<%= f.number_field :mark1 %>
</div>
<div class="field">
<%= f.label 'Main' %><br>
<%= f.text_area :input2 %>
<%= f.number_field :weight2 %>
<%= f.number_field :mark2 %>
</div>
<div class="field">
<%= f.label 'Conclusion' %><br>
<%= f.text_area :input3 %>
<%= f.number_field :weight3 %>
<%= f.number_field :mark3 %>
</div>
<div class="actions">
<%= f.submit class:"btn-xs btn-success"%>
</div>
<% end %>
Mark model
class Mark < ActiveRecord::Base
belongs_to :student
end
Student Model
class Student < ActiveRecord::Base
has_many :marks
has_attached_file :document
validates_attachment :document, :content_type => { :content_type => %w(application/pdf) }
end
It's probably something really stupid but if anyone could explain the problem I'd be really grateful.
Thanks
I don't suggest you using hidden fields for this purpose.
You should pass student together with mark into form_for helper and rails will generate proper url for you which will look like: /students/:student_id/marks
In this case it will be possible to extract student_id from params in your action later.
form_for [#student, #mark], html: {multipart: true} do |f|
More information about nested resources:
http://guides.rubyonrails.org/routing.html#nested-resources
http://www.informit.com/articles/article.aspx?p=1671632&seqNum=7
https://gist.github.com/jhjguxin/3074080
UPDATE:
Forgot to mention that in order to make this work you need to pass student instance into your template at new action:
def new
#student = Student.find(params[:student_id])
#mark = #student.marks.build
end

Rails, Cocoon, Nested form not saving correctly

Hi I have a nested form with rails and cocoon. I tested everything for hours and tried all suggestions in related topics but nothing worked. Here is my setup:
student.rb (Studen Model)
has_many :parents, dependent: :destroy
accepts_nested_attributes_for :parents,
reject_if: proc { |attributes|
attributes['first_name'].blank? },
:allow_destroy => true
students/new.html.erb
<div class="content <% if !#active_tab %> active <% end %>" id="tabStudent">
<%= form_for #student, validate: true do |student| %>
<%= render 'student_fields', model: student %>
<%= hidden_field(:active, 'tab', value: #active_tab) %>
</div>
<div class="content <% if #active_tab === 'tabParents' %> active <% end %>" id="tabParents">
<h2>Parents</h2>
<div id="parents">
<%= student.fields_for :parents do |parent| %>
<%= render partial: 'parent_fields', locals: {include_mode: 'new', f: parent, added_by: ''} %>
<% end %>
<%= link_to_add_association 'add', student, :parents,
render_options: { locals: {include_mode: 'new', added_by: 'rails'}},
'data-association-insertion-node' => '#parents',
'data-association-insertion-method' => 'append' %>
</div>
</div>
students_controller.rb
def create
#student = Student.new(student_params)
#student.parents.build
if #student.save
flash[:success] = "Success"
redirect_to #student
else
render 'new'
end
end
private
def student_params
params.require(:student).permit(:first_name, :last_name, :gender,
parents_attributes: [:id, :first_name, :last_name, :gender, :_destroy])
end
students/_parent_fields.html.erb
<div class="nested-fields">
<div class="row links">
<div class="large-12 columns right">
<%= link_to_remove_association raw('<i style="color:#ff0000;" class="fi-x-circle"></i>'), f %>
</div>
</div>
<div class="row">
<fieldset>
<legend>Data</legend>
<div class="row">
<div class="large-3 columns field">
<%= f.label :gender, 'Student Gender' %>
<%= f.select(:gender, [['male', 'm'], ['female', 'f']]) %>
</div>
<div class="large-3 columns field">
<%= f.label :first_name, 'First Name' %>
<%= f.text_field :first_name %>
</div>
<div class="large-3 columns field">
<%= f.label :last_name, 'Last Name' %>
<%= f.text_field :last_name %>
</div>
</fieldset>
</div>
</div>
It works so far that I can add and remove the form fields but when I send it, only the first parent is saved to the database. If I add a second parent with the "add" link an empty record is saved to the db. The second form also doesn't show up in the post data so it's no surprise that the record is empty but I just can't figure out why the data is not transmitted correctly.
EDIT: I located the problem. I use curb-foundation for the css styling and for the nested form I use their "tabs":
<div class="tabs-content">
<div class="content <% if !#active_tab %> active <% end %>" id="tabStudent">
<%= form_for #student, validate: true do |student| %>
<%= render 'student_fields', model: student %>
<%= hidden_field(:active, 'tab', value: #active_tab) %>
</div>
<div class="content <% if #active_tab === 'tabParents' %> active <% end %>" id="tabParents">
<h2>Parents</h2>
<div id="parents">
<%= student.fields_for :parents do |parent| %>
<%= render partial: 'parent_fields', locals: {include_mode: 'new', f: parent, added_by: ''} %>
<% end %>
<%= link_to_add_association 'new', student, :parents,
render_options: { locals: {include_mode: 'new', added_by: 'rails'}},
'data-association-insertion-node' => '#parents',
'data-association-insertion-method' => 'append' %>
</div>
</div>
The nested divs seem to break cocoon's function. If I remove the divs it works. Does anybody still know how I can work out a solution that allows me to use tabs?
Riddle solved. The empty record was produced because of the #student.parents.build. Cocoon does not require this command.
The main problem was solved by moving the #form_for part out of the first foundation tab one layer up so it would show up above every tab. Then I wrote a little query function to only present it to the user in the first tab.

Facing issue in fetching state names using carmen gem

Hi I am using gem 'carmen-rails' in my app I am having this type of association
class Employee < ActiveRecord::Base
has_one :contact
has_one :permanent_address
accepts_nested_attributes_for :contact, :permanent_address
end
and in my employess_controller.rb I have define like this:
def subregion_options
render partial: 'subregion_select'
end
and in my routes :
resources :employees do
collection do
get 'subregion_options'
end
end
In my employees/new.html.erb
<%= form_for #employee do |f| %>
<label class="form_label">Email</label>
<%= f.text_field :email %>
<label class="form_label">Set Ex Employee</label>
<%= f.check_box :is_active %>
<%= f.fields_for :contact do |c| %>
<label class="form_label">Address 1</label>
<%= c.text_field :current_address1 %>
<%= c.country_select :country,:prompt => 'Select Country' %>
<%= render partial: 'subregion_select', locals: {parent_region: c.object.country} %>
<% end %>
<%= f.submit %>
<% end %>
in employees/_subregion_select.html.erb
<div id="order_state_code_wrapper">
<% parent_region ||= params[:parent_region] %>
<% country = Carmen::Country.coded(parent_region) %>
<% if country.nil? %>
<em>Please select a country above</em>
<% elsif country.subregions? %>
<%= subregion_select(:contact, :state_code, parent_region) %>
<% else %>
<%= text_field(:contact, :state_code) %>
<% end %>
</div>
In js file
$('select#employee_contact_attributes_country').change(function() {
selectWrapper = $('#order_state_code_wrapper');
countryCode = $(this).val();
url = "/employees/subregion_options?parent_region=" + countryCode;
selectWrapper.load(url);
});
But its not fetching any states. Please guide how to solve this. Thanks in advance.

Rails 4 - Nested Object Won't Save

Note: I've read a couple posts similar to this. But non of the solutions answer my question
I have two objects, Bid and Moz. When I build my Bid object, everything seems to save okay, except for the Moz objects.
Model
class Bid < ActiveRecord::Base
belongs_to :user
has_many :mozs, :dependent => :destroy
accepts_nested_attributes_for :mozs, :allow_destroy => true
end
class Moz < ActiveRecord::Base
belongs_to :bid
end
Bids::Build Controllers
class Bids::BuildController < ApplicationController
include Wicked::Wizard
steps :intro, :problems, :solutions, :pricing
def show
#bid = Bid.find(params[:bid_id])
render_wizard
end
def update
#bid = Bid.find(params[:bid_id])
#bid.attributes = build_params
4.times { #bid.mozs.build } if step == steps.second
render_wizard #bid
end
def new
#bid = Bid.new
redirect_to wizard_path(steps.first, :bid_id => #bid.id)
end
def build_params
params.require(:bid).permit(:client_name, :intro, :prob1, :prob2, :prob3, :site_feel, :search_phrase, :page_score, :total_links,
:internal_links, :external_links, :competition, :complete, :user_id, :us_company, :philosophy_1,
:philosophy_2, :website_conclusions, :is_onsite_seo, :onsite_seo, :is_ongoing_seo, :ongoing_seo,
:is_ppc, :ppc, :is_social_media, :social_media, :is_google_places, :google_places, :is_adwords_express,
:adwords_express, moz_attributes: [:url, :id, :_destroy]
)
end
private
def finish_wizard_path
root_url
end
end
solutions.html.erb
<%= form_for (#bid), url: wizard_path do |f| %>
<% if #bid.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#bid.errors.count, "error") %> prohibited this bid from being saved:</h2>
<ul>
<% #bid.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% if #bid.is_onsite_seo? %>
<div class="field">
<%= f.label :onsite_seo %><br>
<%= f.text_area :onsite_seo %>
</div>
<% end %>
<% if #bid.is_ongoing_seo? %>
<div class="field">
<%= f.label :ongoing_seo %><br>
<%= f.text_area :onsite_seo %>
</div>
<% end %>
<div class="field">
<%= f.label :ppc %><br>
<%= f.text_area :ppc %>
</div>
<div class="field">
<%= f.label :social_media %><br>
<%= f.text_area :social_media %>
</div>
<div class="field">
<%= f.label :google_places %><br>
<%= f.text_area :google_places %>
</div>
<div class="field">
<%= f.label :adwords_express %><br>
<%= f.text_area :adwords_express %>
</div>
<%= f.fields_for :mozs do |builder| %>
<%= render partial: "moz_fields", locals: {f: builder} %>
<% end %>
<%= link_to_add_association "Add URL", f, :mozs %>
<div class="actions">
<%= f.submit %>
or <%= link_to "skip this step", next_wizard_path %>
</div>
<% end %>
_moz_fields.html.erb
<div class="field fields">
<%= f.label :url, "Comparative URL" %><br>
<%= f.text_field :url %>
<%= f.hidden_field :destroy %>
<%= link_to_function "remove", "remove_fields(this)"%>
</div>
I don't understand why they won't save. In addition, I noticed something odd -- when I don't use a partial for the nested object and use the f form builder for the #bid object (as opposed to 'builder'), I get an error no method or variable :url, but a Moz object is saved (although, not with any of the desired attributes).
My opinion that you misspelled with permit attrbibutes hash, try to change moz_attributes to mozs_attributes.
params.require(:bid).permit(..., :mozs_attributes: [:url, :id, :_destroy])
If you send the parameter _destroy: 1 through your hidden field
<%= f.hidden_field :destroy %>
you instruct Rails to destroy the child moz object, or in your case, prevent it from being created.
As for the second part of your question, if you inline the partial from this
<%= f.fields_for :mozs do |builder| %>
<%= render partial: "moz_fields", locals: {f: builder} %>
<% end %>
to this
<%= f.fields_for :mozs do |builder| %>
<div class="field fields">
<%= f.label :url, "Comparative URL" %><br>
<%= f.text_field :url %>
<%= link_to_function "remove", "remove_fields(this)"%>
</div>
<% end %>
it won't work, because the model object for the scope f is your #bid, not moz. Bids have no url attribute, hence the error.
With the input fields being created in the wrong form builder scope, you did not actually transmit any attributes for your moz object, and so it was created blank. As a side effect, this also meant not sending the _destroy parameter, so the object was saved.
Instead, inline the partial like this (I renamed builder to moz for clarity):
<%= f.fields_for :mozs do |moz| %>
<div class="field fields">
<%= moz.label :url, "Comparative URL" %><br>
<%= moz.text_field :url %>
<%= link_to_function "remove", "remove_fields(this)"%>
</div>
<% end %>

Resources