Im new to ruby on rails so i need some tips please.
Im trying to render some checkboxes on the edit view for a user. I have tried to follow the documentation for the nested_attributes but the checkboes does not render. Here is the relation between the two models:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :softwares
has_many :courses
accepts_nested_attributes_for :softwares
accepts_nested_attributes_for :courses
The Edit view for a user
<div class="container">
<div class="row">
<div class="col-md-12 mt-5">
<%= form_for @student, url: auth_student_path(@student), method: :put do |f| %>
<div class="col-md-12 mb-5 mt-5">
<div class="row">
<h3 class="mb-3 filter_heading">Softwares</h3>
<% @softwares.each do |sf|%>
<%= f.fields_for :softwares do |software| %>
<div class="col-md-3">
<div class="courses">
<%= software.label sf.title %>
<%= software.check_box :title, {multiple: true}, sf.title, nil %>
</div>
<% sf.courses.each do |crs|%>
<%= f.fields_for :courses do |course|%>
<div class="mt-1 courses-checkbox">
<%= course.label crs.name %>
<%= course.check_box :name, {multiple: true}, crs.name , nil %>
</div>
<% end %>
<% end%>
</div>
<% end %>
<% end%>
</div>
<div class="form-group">
<%= f.submit "Save", class:"btn btn-primary"%>
</div>
<% end %>
</div>
</div>
</div>
The Controller
module Auth
class StudentsController < ApplicationController
before_action :authenticate_user!
before_action :set_student, only: %i[delete_certificates]
def edit
authorize! :edit, @user
@softwares = Software.all
@student = User.find(params[:id])
end
def update
authorize! :update, @user
@student = User.find(params[:id])
if @student.update(student_params)
redirect_to edit_auth_student_path(@student)
else
redirect_to edit_auth_student_path(@student)
end
end
def show
def set_student
@student = User.find(params[:student_id])
end
private
def student_params
params.require(:user).permit(
:email,
:firstname,
:lastname,
:phone,
:locked,
:approved,
:role,
badges: [],
certificates: [],
softwares_attributes: [:title],
courses_attributes: [:name],
)
end
end
end
Please help me.
You don't need accepts_nested_attributes_for
just to select existing records and associate them with something. Its only needed if you need to create/update the other record (the course or software) at the same time.
I'm also guessing you don't actually want to have a one-to-many assocation and duplicate every course and every software for each user - instead you want a many to many assocation and some data normalization.
So create a join table to hold the assocation between users and courses for example:
class User < ApplicationRecord
has_many :enrollments, foreign_key: :student_id
has_many :courses, through: :enrollments
end
# rails g model enrollment student:belongs_to course:belongs_to
class Enrollment < ApplicationRecord
belongs_to :student, class_name: 'User'
belongs_to :course
end
class Course < ApplicationRecord
has_many :enrollments
has_many :students, through: :enrollments
end
And then you just create inputs that use the course_ids
/ course_ids=
setter and getter created by has_many :courses, through: :enrollments
.
<%= form_with(model: @student, url: auth_student_path(@student), method: :put) do |f| %>
<div class="field">
<%= f.label :course_ids, 'Select your courses' %>
<%= f.collection_select :course_ids, @courses, :id, :name, multiple: true %>
</div>
# ...
<% end %>
And then you just whitelist an array of ids in your controller:
params.require(:user)
.permit(
# ...
course_ids: []
)
In fact if your ever passing existing records as anything but an ID you're doing it very wrong.
There are still plenty of issues with this code but this should at least be nudge in the correct direction.