fields_for doesn't save object sin rails - ruby-on-rails

Im interested why does my nested form in RoR doesnt save child objects :(
For now, it just save the Parent (Printer) value and makes the child(Color) disappear on second render (error)! What Am I doing wrong?
Parent model
class Printer < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
validates :model, presence: true
has_many :colors, dependent: :destroy
accepts_nested_attributes_for :colors
end
Child model
class Color < ActiveRecord::Base
belongs_to :printer
validates :color, presence: true
end
View (new.html.erb)
<%= form_for #printer do |p|%>
<%= p.text_field :model %>
<%= p.fields_for :colors do |color|%>
<%= color.text_field :color%>
<% end %>
<%= p.submit "Add"%>
<% end %>
And controller
def create
#printer = current_user.printers.build(printer_params)
if #printer.save
redirect_to #current_user
else
render 'new'
end
end
def new
#printer = Printer.new
#printer.colors.build
end
private
def printer_params
params.require(:printer).permit(:model)
end
Edit:
This one helps
private
def printer_params
params.require(:printer).permit(:model, colors_attributes: [:color])
end

When using nested forms, you need to specify which nested attributes should be whitelisted:
def printer_params
params.require(:printer).permit(:model, colors_attributes: [:color])
end
You can read more about it at Rails Guides - Form Helpers - the controller section and at RoR API documentation - Strong Parameters

Related

Request parameters not saving on model_params on n:n relationship with check_box_tag

I am using Rails 5.1 and im having some issues saving params on an n:n relationship.
I have three models:
class Course < ApplicationRecord
belongs_to :studio
has_many :reviews, dependent: :destroy
has_many :has_category
has_many :categories, through: :has_category
validates :name, presence: true
end
class Category < ApplicationRecord
has_many :has_category
has_many :courses, through: :has_category
end
class HasCategory < ApplicationRecord
belongs_to :category
belongs_to :course
end
and a simple form to create a new course with different categories using check_box_tag (not sure if using it correctly though)
<%= simple_form_for [#studio, #course] do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<% #categories.each do |category| %>
<%= check_box_tag "course[category_ids][]", category.id, true %>
<%= category.name%>
<% end %>
<%= f.button :submit %>
<% end %>
And all is permitted and created on the courses controller:
def new
#studio = Studio.find(params[:studio_id])
#course = Course.new
#course.studio = #studio
#categories = Category.all
end
def create
#studio = Studio.find(params[:studio_id])
#course = Course.new(course_params)
#course.studio = #studio
#categories = params[:category_ids]
if #course.save
redirect_to course_path(#course)
else
render :new
end
end
def course_params
params.require(:course).permit(:studio_id, :name, :description, :category_ids)
end
With better_errors i know the categories are being requested, here the request info:
"course"=>{"name"=>"Course test", "description"=>"testing", "category_ids"=>["2", "3"]}, "commit"=>"Create Course", "controller"=>"courses", "action"=>"create", "studio_id"=>"16"}
but the categories are not saved on course_params, HasCategory instance or on the Course, i´ve tried with #course.categories = params[:category_ids] and other solutions without success.
How do i save the categories to the courses?
Try the following
Change the strong parameter with category_ids: []
def course_params
params.require(:course).permit(:studio_id, :name, :description, category_ids: [])
end
Comment out this line #categories = params[:category_ids]
Hope it helps

Validate presence of fields from another model

With the following associations:
class Profile < ActiveRecord::Base
belongs_to :visitor
belongs_to :contact_point
validates :contact_point, presence: true
end
class Visitor < User
has_one :profile, dependent: :destroy
has_one :contact_point, through: :profile
end
class ContactPoint < User
has_many :profiles
has_many :visitors, through: :profiles
end
Each ContactPoint has a email. When the visitor creates her profile using the following form, she needs to determine the profiles contact point using the email address belonging to ContactPoint. The contact point users are already created and the visitors should not be able to update ContactPoint model.
<%= form_for #profile do |f| %>
<%= f.label 'First Name' %>
<%= f.text_field :first_name %>
<%= f.label 'Last Name' %>
<%= f.text_field :last_name %>
<%= fields_for :contact_point, #profile.contact_point do |ff| %>
<%= ff.label 'Contact point email' %>
<%= ff.text_field :email %>
<% end %>
<% end %>
In ProfilesController I am passing parameters to profile model this way:
def create
#profile = Profile.create(profile_params)
end
def profile_params
contact_point = ContactPoint.find_by_email(params[:contact_point][:email])
params.require(:profile).permit(:first_name, :last_name)
.merge(visitor_id: current_user.id, contact_point: contact_point)
end
With the above setup, when there is no ContactPoint with the provided email address, the contact_point variable will set to be nil and the validator can't distinguish whether the contact point email in the filled in from was empty or not.
Now, how can I add a validation to check the presence of this email address in contact_points table and show a custom error message?
You would have to do it yourself in your controller, something like:
def create
#profile = Profile.create(profile_params)
if !#profile.contact_point
if params[:contact_point][:email].present?
#profile.errors.add(:contact_point, 'No contact with found')
else
#profile.errors.add(:contact_point, 'Please provide an email')
end
render :new
end
end
The best would be to use a custom validation that checks if contact_pounts.email is blank? If yes, then return false.
EDIT:
My brain is functioning better now after some sleep. You can do this using rails. This is how I would do it.
class Profile < ActiveRecord::Base
belongs_to :visitor
belongs_to :contact_point
validates :contact_point, presence: true
accepts_nested_attributes_for :contact_point
end
class Visitor < User
has_one :profile, dependent: :destroy
has_one :contact_point, through: :profile
end
class ContactPoint < User
has_many :profiles
has_many :visitors, through: :profiles
validates :email, presence: true
end
What is going on here? We accept nested attributes for the association (ContactPoint) from Profile, so we can pass them through the #profile form you have to the controller. The models will handle the validation and set the error messages accordingly.
Does this makes sense?

has_one nested association nullifies foreign key on edit route

I am building a nested form in ruby on rails.
The addition of a nested has_one association works fine. However, when I load the edit page, the foreign key company_id of the nested association is nullified.
I have tried update_only: true in accepts_nested_attributes_for and including :id in strong params as suggested in other similar answered questions on stackoverflow but nothing works for me.
Could anyone tell me what is actually causing the nested association to update and nullify its foreign key itself? My codes are as shown below. Thanks!
# company.rb
class Company < ApplicationRecord
has_one :mission
accepts_nested_attributes_for :mission, update_only: true
end
# mission.rb
class Mission < ApplicationRecord
belongs_to :company, optional: true
validates :description, presence: true, length: { maximum: 100 }
end
# companies_controller.rb
class CompaniesController < ApplicationController
def edit
#company = Company.find(params[:id])
#company.build_mission if #company.build_mission.nil?
end
def update
#company = Company.find(params[:id])
#company.assign_attributes(company_params)
if #company.valid?
#company.save
redirect_to companies_path
end
end
private
def company_params
params.require(:company).permit(mission_attributes: [:id, :description, :_destroy])
end
end
# edit.html.erb
<%= form_for #company, :url => company_path(#company), :html => {class: 'ui form', method: :put} do |f| %>
<%= f.fields_for :mission do |mission| %>
<div class="field">
<%= mission.label :mission %>
<%= mission.text_field :description %>
</div>
<% end %>
<%= f.button :submit => "", class: "ui button" %>
<% end %>
Hey I manage to solve the problem after a good sleep. Turns out i just have to play around with the if else condition at the companies controller level. The edit method should be amended to such:-
def edit
#company = Company.find(params[:id])
if #company.mission
else
#company.build_mission
end
end

Rails nested fields creation in loop

I have three model classes related to each other.
class Student < ActiveRecord::Base
has_many :marks
belongs_to :group
accepts_nested_attributes_for :marks,
reject_if: proc { |attributes| attributes['rate'].blank?},
allow_destroy: true
end
This class describes a student that has many marks and I want to create a Student record along with his marks.
class Mark < ActiveRecord::Base
belongs_to :student, dependent: :destroy
belongs_to :subject
end
Marks are related both to the Subject and a Student.
class Subject < ActiveRecord::Base
belongs_to :group
has_many :marks
end
When I try to create the nested fields of marks in loop labeling them with subject names and passing into in it's subject_id via a loop a problem comes up - only the last nested field of marks is saved correctly, whilst other fields are ignored. Here's my form view code:
<%= form_for([#group, #student]) do |f| %>
<%= f.text_field :student_name %>
<%=f.label 'Student`s name'%><br>
<%= f.text_field :student_surname %>
<%=f.label 'Student`s surname'%><br>
<%=f.check_box :is_payer%>
<%=f.label 'Payer'%>
<%= f.fields_for :marks, #student.marks do |ff|%>
<%#group.subjects.each do |subject| %><br>
<%=ff.label subject.subject_full_name%><br>
<%=ff.text_field :rate %>
<%=ff.hidden_field :subject_id, :value => subject.id%><br>
<%end%>
<% end %>
<%= f.submit 'Add student'%>
<% end %>
Here`s my controller code:
class StudentsController<ApplicationController
before_action :authenticate_admin!
def new
#student = Student.new
#student.marks.build
#group = Group.find(params[:group_id])
#group.student_sort
end
def create
#group = Group.find(params[:group_id])
#student = #group.students.new(student_params)
if #student.save
redirect_to new_group_student_path
flash[:notice] = 'Студента успішно додано!'
else
redirect_to new_group_student_path
flash[:alert] = 'При створенні були деякі помилки!'
end
end
private
def student_params
params.require(:student).permit(:student_name, :student_surname, :is_payer, marks_attributes: [:id, :rate, :subject_id, :_destroy])
end
end
How can I fix it?
#student.marks.build
This line will reserve an object Mark.
If you want multi marks, May be you need something like this in new action :
#group.subjects.each do |subject|
#student.marks.build(:subject=> subject)
end
Hope useful for you.

Nested Forms Rails 4.0 Strong Parameters

Been trying this all night and I can't get the photo upload to work. The 2 tables work just fine but no dice on a polymorphic table that holds photos. Any fresh new eyes would be such great help.
def restaurant_params
params.require(:restaurant).permit(:res_name, :res_description, restaurant_branches_attributes: [ :id, :address_line1, :address_line2, :address_line3, :address_line4, :address_line5, :address_line6, :number_phone, :number_fax, :email, :_destroy ], pictures_attributes: [ :id, :name, :image] )
end
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
mount_uploader :image, ImageUploader
end
class Restaurant < ActiveRecord::Base
has_many :restaurant_branches
accepts_nested_attributes_for :restaurant_branches, allow_destroy: true
end
class RestaurantBranch < ActiveRecord::Base
belongs_to :restaurants
has_many :pictures, as: :imageable
accepts_nested_attributes_for :pictures, allow_destroy: true
before_save :set_address
def set_contact_info
contact_info = "Phone: #{self[:number_phone]} Fax: #{self[:number_fax]} Email: #{self[:email]}"
end
def set_address
address = self.address_line2.nil? ? partial_address.titleize : complete_address.titleize
end
private
def complete_address
address = "#{self[:address_line1]} #{self[:address_line2]} #{self[:address_line3]} #{self[:address_line4]} #{self[:address_line5]} #{self[:address_line6]}"
end
def partial_address
address = "#{self[:address_line1]} #{self[:address_line3]} #{self[:address_line4]} #{self[:address_line5]} #{self[:address_line6]}"
end
end
Multi-Level Association
The polymorphic nature of the associations shouldn't be a problem, as Rails will typically send the data to the association - in this case pictures
I think your problem is more to do with the multi-level association you have, specifically that you need to pass the attributes as follows [form submit] > RestaurantBranch > Pictures
--
We've done this before, and here's how you do it:
#app/controllers/restaurants_controller.rb
Class RestaurantsController < ApplicationController
def new
#restaurant = Resaurant.new
#restaurant.restaurant_branches.build.pictures.build #-> notice multi-level nesting
end
def create
#restaurant = Restaurant.new(restaurant_params)
#restaurant.save
end
private
def restaurant_params
params.require(:restaurant).permit(:res_name, :res_description, restaurant_branches_attributes: [ :id, :address_line1, :address_line2, :address_line3, :address_line4, :address_line5, :address_line6, :number_phone, :number_fax, :email, :_destroy, pictures_attributes: [ :id, :name, :image]])
end
end
#app/views/restaurants/new.html.erb
<%= form_for #restaurant do |f| %>
<%= f.fields_for :restaurant_branches do |rb| %>
<%= rb.fields_for :pictures do |p| %>
<%= p.file_field :image %>
<% end %>
<% end %>
<% end %>

Resources