rails 5.1: nested form validation - ruby
I have a parent model called user and 2 nested models called award and certification. when I go to save a certification the model validation for award tells me that awards is blank, and when I got to save an award I get an error that says the validations for certification is blank. not sure what's going on.
here my user model
has_many :certifications, dependent: :destroy
has_many :awards, dependent: :destroy
# allows for nested attributes in the user views.
accepts_nested_attributes_for :work_histories, :qualifications, :certifications, :courses, :awards,
:languages, :patents, :publications, allow_destroy: true
here my award model
class Award < ApplicationRecord
belongs_to :user
validates :award_name, :award_granted_by, :award_date, :award_description, presence: true
end
and my certification model
class Certification < ApplicationRecord
belongs_to :user
validates :certification_name, :certification_authority, :certification_number, :certification_from,
:certification_to, presence: true
end
strong params in users_controller
def user_params
params.require(:user).permit( :first_name, :last_name, :email, :phone, :current_job_title, :password, :password_confirmation,
work_histories_attributes: [:user_id, :id, :_destroy, :job_title, :company, :description, :city, :state,:start_date, :end_date],
qualifications_attributes: [:user_id, :id, :_destroy, :education, :university, :university_major, :graduation_year, :currently_enrolled, :location],
awards_attributes: [:user_id, :id, :_destroy, :award_name, :award_granted_by, :award_date, :award_description],
certifications_attributes: [:user_id, :id, :_destroy, :certification_name, :certification_authority, :certification_number, :certification_from, :certification_to, :cert_never_expires],
courses_attributes: [:user_id, :id, :_destroy, :course_name, :course_number, :course_description],
languages_attributes: [:user_id, :id, :_destroy, :language, :language_proficiency],
patents_attributes: [:user_id, :id, :_destroy, :patent_title, :patent_office, :patent_number, :patent_status, :patent_date, :patent_description],
publications_attributes: [:user_id, :id, :_destroy, :publication_title, :publication_source, :publication_date, :publication_description] )
end
here's a part of the User form. I've got a button dropdown that has several options, when a user selects an option that particular div will be shown with a modal window.
<!-- one-to-many nested attributes -->
<%= form_for(#user) do |form| %>
<!-- a partial is rendered based on the user dropdown selection -->
<div id="award">
<%= render partial: "awards/new_award_modal", locals: { form: form } %>
</div>
<div id="cert">
<%= render partial: "certifications/new_certification_modal", locals: { form: form } %>
</div>
<%= form.submit 'Save', id: "submit-achievement", class: 'btn btn-primary form-control' %>
</div><!-- modal body -->
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
<% end %><!-- form_for -->
here is the partial for certifications
<div class="new_cert">
<%= form.fields_for :certifications, Certification.new do |cert_field| %>
<div class="field">
<%= cert_field.label :certification_name, 'Certification Title' %>
<%= cert_field.text_field :certification_name, class: "form-control" %>
</div>
<div class="field">
<%= cert_field.label :certification_authority %>
<%= cert_field.text_field :certification_authority, class: "form-control" %>
</div>
<div class="field">
<%= cert_field.label :certification_number %>
<%= cert_field.text_field :certification_number, class: "form-control" %>
</div>
<table>
<tr>
<td>
<div class="field">
<%= cert_field.label :certification_from, "From" %><br />
<%= cert_field.date_select :certification_from,{ :order => [:day, :month, :year],
:start_year => Date.current.year, :end_year => 1960,
:prompt => {day: 'Day', month: 'Month', year: 'Year'}, :discard_day => true }, class: 'form-control' %>
</div>
</td>
<td> </td>
<td>
<div class="field">
<%= cert_field.label :certification_to, "To" %><br />
<%= cert_field.date_select :certification_to, { :order => [:day, :month, :year],
:start_year => Date.current.year + (10), :end_year => 1960,
:prompt => {day: 'Day', month: 'Month', year: 'Year'}, :discard_day => true }, class: 'form-control' %>
</div>
</td>
</tr>
</table>
<div>
<%= cert_field.label :cert_never_expires, "This certification does not expire", class: "l" %>
<%= cert_field.check_box :cert_never_expires, class: 'cb' %>
</div>
<br />
<% end %>
</div><!-- ./new_cert -->
here's the awards partial
<div class="new_award">
<!-- nested attributes for award modal -->
<%= form.fields_for :awards, Award.new do |award_field| %>
<div class="field">
<%= award_field.label :award_name, 'Award or Honor Name' %>
<%= award_field.text_field :award_name, class: "form-control" %>
</div>
<div class="field">
<%= award_field.label :award_granted_by, "Granted by" %>
<%= award_field.text_field :award_granted_by, class: "form-control" %>
</div>
<div class="field form-inline">
<%= award_field.label :award_date, 'Date' %><br />
<%= award_field.date_select :award_date, { :order => [:day, :month, :year],
:start_year => Date.current.year, :end_year => 1960,
:prompt => {day: 'Day', month: 'Month', year: 'Year'},
:discard_day => true }, class: 'form-control ds' %>
</div>
<div class="field">
<%= award_field.label :award_description, 'Description' %>
<%= award_field.text_area :award_description, class: "form-control" %>
</div>
<% end %><!-- ./fields_for -->
</div>
so both awards and certifications can be saved to the database without any issue when there are no validations. however, when I just do a simple validation checking for presence of an attribute in the form that's when things get weird. The award model is saying that the certification form is empty, which it should be because I'm submitting the award form. And the opposite is also true, when I go to submit a certification the award validation is triggered saying the award form is blank.
It would be best to see the specific error you're getting, but I'd make sure that your form is setting the nested attributes appropriately, and that your user controller has nested attributes setup within the strong parameters.
There's documentation in the Rails guides: http://api.rubyonrails.org/classes/ActionController/StrongParameters.html
Related
Rails 5: Is Nested forms set up correct
If I want a Nested Form for volunteer_shift inside my assignment/_form How should my model associations be set up? My current associations look like this..but I am not sure they are correct if the volunteer_shift partial sits inside the assignment form.... When I try to open the "add new record" form...my assignment#new action errors out... error message Completed 500 Internal Server Error in 30ms (ActiveRecord: 2.7ms) NoMethodError - undefined method `build' for nil:NilClass: app/controllers/assignments_controller.rb:21:in `new' Here is my new action... # GET /assignments/new def new #assignment = Assignment.new # binding.pry #assignment.volunteer_event.build <----Line 21 #my_url = {:action => "create", :id => params[:id]} end Here are my current models associations... class Assignment < ApplicationRecord attr_accessor :volunteer_event belongs_to :volunteer_shift has_one :volunteer_task_type, :through => :volunteer_shift, :source => :volunteer_task_type belongs_to :contact ,optional: true validates_presence_of :volunteer_shift #belongs_to takes care of this now validates_associated :volunteer_shift accepts_nested_attributes_for :volunteer_shift, allow_destroy: true ... class VolunteerShift < ApplicationRecord has_many :assignments belongs_to :volunteer_event ... class VolunteerEvent < ApplicationRecord belongs_to :volunteer_default_event has_many :volunteer_shifts, :dependent => :destroy has_many :resources_volunteer_events, :dependent => :destroy validates_associated :volunteer_shifts And inside my assignment/form <%= form_for #assignment, :url => #my_url, remote: true do |f| %> ... <!--VOLUNTEER SHIFT--> <!--TODO: make this a partial under field_for--> <%= f.fields_for :volunteer_shift do |builder| %> <%= render 'volunteer_shift_fields', vs: builder %> <% end %> ... <% end %> and if you want to see the volunteer_shift_fields partial <div class="name large flex-row"> <%= vs.label :volunteer_shift %> </div> <div id="volunteer_shift" class="d-flex flex-row"> <div class="col-sm-12 p-2"> <div id="volunteer_shift" class="text-right"> <!-- old if: if class is assignment show volunteer shift else show default shift --> <!-- we need default shift here...NO assignment is attached--> <div class="field"> <%= vs.label :volunteer_task_type_id %> <%= select_tag 'volunteer_task_type_id', options_from_collection_for_select([VolunteerTaskType.new(:description => ""), VolunteerTaskType.instantiables.effective_on(Date.today)].flatten, "id", "description") %> </div> <div class="field"> <%= vs.label :roster_id %> <%= select_tag 'roster_id', options_from_collection_for_select([Roster.new(:name => ""), Roster.all].flatten, "id", "name") %> </div> <div class="field"> <%= vs.label :program_id %> <%= select_tag 'program_id', options_from_collection_for_select([Program.new(:name => ""), Program.where(:volunteer => true)].flatten, "id", "name")%> </div> <div class="field"> <%= vs.label :set_description %> <%= vs.text_field(:set_description, nil) %> </div> <div class="field"> <%= vs.label :set_date, "Date" %> < <%= vs.text_field(:set_date, nil) %> </div> </div> </div> </div> Are my model associations correct? What is my "assignment#new" action missing?
Remove attr_accessor :volunteer_event, you need to set up the model association to volunteer_event through volunteer_shift so class Assignment < ApplicationRecord attr_accessor :volunteer_event needs to become class Assignment < ApplicationRecord has_one :volunteer_event, through: :volunteer_shift Looking at your volunteer_shift associations I'm pretty sure that you only want to ever have a single volunteer_event record for a single assignment? if that's not what you want then you need to look at your associations again Or... You could change the attr_accessor to attr_reader and assign the relevant volunteer_event record in an initializer but I would not recommend that approach for this case
How to get parameters value in model from views form in rails?
Here is my new.html.erb <%= form_for :simulation, url: simulations_path do |f| %> <div class="form-group"> <%= f.label :Name %> <div class="row"> <div class="col-sm-2"> <%= f.text_field :name, class: 'form-control' %> </div> </div> </div> <div class="form-group"> <%= f.label :'Rendering Option' %> <div class="Dropdown"> <div class="col-sm-4"> <%= select_tag(:is_random, options_for_select([['Random', true], ['No Opinion', false]], selected: :is_random )) %> </div> </div> <div class="row"> <div class="col-sm-6"> <%= f.submit 'Submit', class: 'btn btn-primary' %> </div> simulations_controller.rb class SimulationsController < ApplicationController def index #simulations = Simulation.all end def new end def create #simulation = Simulation.new(simulation_params) #simulation.save redirect_to #simulation end def show #simulation = Simulation.find(params[:id]) end end Simulation.rb (Model class) class Simulation < ActiveRecord::Base belongs_to :user end Schema.rb create_table "simulations", force: :cascade do |t| t.string "name" t.boolean "is_random" end I am not able to set the :is_random value in database while rest is fine. What I am doing wrong here? I checked the value in sqlite database and there was null entry in is_random column.
You need to permit attributes while doing mass assignment. You could write it as : <%= f.select(:is_random, options_for_select([['Random', true], ['No Opinion', false]], selected: :is_random )) %> or <%= select_tag("simulations[:is_random]", options_for_select([['Random', true], ['No Opinion', false]], selected: :is_random )) %> With your syntax, the value is inside the params hash as {..., is_random: true,..}, that's why inside the strong parameter filtering method you are not getting it. If you use now the suggested solutions, you will get it the value inside the params hash like {..., simulations: { is_random: true,..}, ...}. You can inspect all these from the the development.log file, while making the request.
You need to use form object select method like bellow: <%= f.select(:is_random, options_for_select([['Random', true], ['No Opinion', false]], selected: :is_random )) %>
Using f.select (suggested above) should fix your problem. If you look at the generated html, you will see this field with a name of "is_random". It should be "simulations[is_random]". When you pull the form field values from the params object like this params[:simulations] all form fields with names in the form of "simulations[name]" will be included. Using the form builder object names the form fields correctly. Hope this helps!
You can also use <%= debug params %> to inspect what's in params, it's very helpful.
Partner() expected, got String()
I have a course model and there is a many to many relationship with category, i am using a multi select on my form to get an array of objects, A course also can have a partner and the user can select this from a single select, but when i want to save to the database I call the params then I get a string instead of an object. not to sure how I can solve this <%= form_for #course do |f| %> <section class="new_course"> <div class="row collapse"> <div class="medium-5 small-centered column"> <%= f.label :course_title, :class=>"custom-prefix-class" %> <%= f.text_field :title, :autofocus => true, :class=>"custom-input-class" %> </div> </div> <div class="row collapse"> <div class="medium-5 small-centered column"> <%= f.label :start_date, :class=>"custom-prefix-class" %> <%= f.text_field :start_date, :class=>"custom-input-class" %> </div> </div> <div class="row"> <div class="medium-5 column with_chosen"> <%= f.collection_select :categories, Category.all ,:id,:name, { include_blank: true}, { class: 'chosen-select', :multiple=>true, :data => { :placeholder => ' ' }} %> </div> </div> <div class="row"> <div class="medium-5 column"> <%= f.collection_select :partner, Partner.all.collect, :id,:name , { include_blank: true }, { class: 'chosen-select', :multiple=>false, :data => { :placeholder => ' Brand Partner' }} %> </div> </div> In my controller def create #course = Course.new(course_params) end if #course.save render :action=>'new' end end private def course_params params.require(:course).permit(:title, :start_date,:duration,:partner,:categories => []) end //rails server output Started POST "/courses" for 127.0.0.1 at 2014-02-07 16:45:28 +0200 Processing by CoursesController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"sKhAF2X6VOXTxZC9Pt51RFQfZSKdzXVWji6x4uwg+rI=", "course"=>{"title"=>"Marketing ", "start_date"=>"14-02-2014", "categories"=>["", "1", "2"], "partner"=>"1", "duration"=>"2-weeks"}, "commit"=>"Add new course"} Completed 500 Internal Server Error in 3ms ActiveRecord::AssociationTypeMismatch (Partner(#70274068591140) expected, got String(#70274066607160)): app/controllers/courses_controller.rb:24:in `create'
try this out:- it seems as that partner is a model and course has_one partner, so you have to assign partner class object instead of id as string actually as you can see in in question above value of params[:course][:partner] => "1" is a string now if you try to do something like #course.partner = '1' it will raise an error like above, as on left hand side we have parter association and right and side a string. just do this def course_params params.require(:course).permit(:title, :start_date,:duration,:partner,:categories => []) if params[:course][:partner].present? params[:course][:partner] = Partner.find params[:course][:partner] end end
how to do a many to many model for this project in rails
I'm having trouble creating a many to many model for my project. Basically i have a Matches & Teams model. Teams are created prior to the Matches. Once the match is created then i would like to add teams to it. Match can have many teams, Teams can have many matches. I'm currently adding teams via nested_form and adding multiple teams at once. When submitting the form, i get an error expecting the team to be in a relationship already with the match. I can do this with a many to one relationship but it fails with many-to-many, was wondering if there was any way to do it without doing a custom route. Below is the form, controllers are as per default values. Form: <%= nested_form_for(#match) do |f| %> <% if #match.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(#match.errors.count, "error") %> prohibited this match from being saved:</h2> <ul> <% #match.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name %> </div> <div class="field"> <%= f.label :date %><br /> <%= f.date_select :date %> </div> <%= f.fields_for :teams, :html => { :class => 'form-vertical' } do |builder| %> <%= builder.label "Team Name:" %> <%= builder.autocomplete_field :name, autocomplete_team_name_teams_path, :update_elements => {:id => "##{form_tag_id(builder.object_name, :id)}" },:class => "input-small",:placeholder => "Search" %> <%= builder.hidden_field :id %> <% end %> <%= f.link_to_add raw('<i class="icon-plus-sign"></i>'), :teams, :class => 'btn btn-small btn-primary' %> </div> <div class="actions"> <%= f.submit %> </div> <% end %>
Using a join model, the has_many :through macro and the accepts_nested_attributes_for macro you can do something like the following. class Match has_many :competitions has_many :teams, :through => :competitions accepts_nested_attributes_for :teams end class Competition belongs_to :match belongs_to :team end class Team has_many :competitions has_many :matches, :through => :competitions end Just make sure your form is set up to send the following data structure as params when the request reaches the create or update controller. params => { :match => { # ... :teams_attributes => [ { :name => 'Foo', :color => 'blue' }, { :name => 'Bar', :color => 'green' }, # ... ] } }
Rails Save File Checkbox
How can I make it so that my info only gets stored if the checkbox is checked. Here is what I have so far: <% #extra.each do |extra| %> <%= f.fields_for :purchaseds do |builder| %> <div class="label-field"> <%= builder.label :name, extra.name %> <p><%= extra.description %></p> </div> <div class="text-field"> $<%= extra.price %> <%= builder.check_box :purchased %> </div> #I WOULD LIKE THIS TO ONLY GET SAVED IF THE CHECK BOX FOR PURCHASED IS CHECKED <%= builder.hidden_field :name, :value => extra.name %> <%= builder.hidden_field :description, :value => extra.description %> <%= builder.hidden_field :price, :value => extra.price %> <% end %> <% end %> My client asked to be able to add extra services himself, and then users could be able to choose if they want to purchase them as accessories to their order. So what I did was I made a table called Extra (for extra services) and another table called Purchased. Purchased belongs to Order and is a nested attribute.
In your purchaseds model add validations: validates_presence_of :name, :description, :price, :if => :purchased Update Add :reject_if option to your purchaseds parent model in accepts_nested_attributes_for :purchaseds, :reject_if => {|attrs| !attrs[:purchased]}