I am trying to use collection_select (using bootstrap_form_for) to save associated record. The save works well using rails console so I am assuming the model definitions are good (and so is the controller code).
Models: User -
class User < ApplicationRecord
validates :name, presence: true, length: {maximum: 50}
validates :email, presence: true, length: {maximum: 250}, uniqueness: true
has_secure_password
validates :password, length: {minimum: 6}
belongs_to :role
has_many :issues_owned, :class_name => 'Issue', :foreign_key => 'owner'
has_many :issues_assigned, :class_name => 'Issue', :foreign_key => 'assigned_to'
end
Model Issue -
class Issue < ApplicationRecord
belongs_to :assigned_to, :class_name => 'User'
belongs_to :owner, :class_name => 'User'
validates :summary, presence: true, length: {maximum: 100}
validates :description, presence: true, length: {maximum: 250}
validates :severity, presence: true, length: {maximum: 10}
validates :priority, presence: true, length: {maximum: 10}
end
Form code for Issue addition -
<%= bootstrap_form_for(@issue, layout: :horizontal, label_col: "col-sm-2") do |form| %>
<%= form.text_field :summary, control_col: 'col-md-4',class: 'input-sm' %>
<%= form.text_area :description, control_col: 'col-md-8', class: 'input-sm' %>
<%= form.text_field :severity, control_col: 'col-md-2', class: 'input-sm' %>
<%= form.text_field :priority, control_col: 'col-md-2', class: 'input-sm' %>
<%= form.collection_select :assigned_to, @users, :id, :name, control_col: 'col-md-4' , class: 'input-sm' %>
<%= form.date_field :resolved_at, control_col: 'col-md-4' , class: 'input-sm'%>
<%= form.hidden_field :owner, control_col: ' col-md-4', value: @current_user %>
<%= form.form_group do %>
<%= form.primary "Create Issue", class:'btn btn-sm btn-primary' %>
<% end %>
<% end %>
Fields of interest are :assigned_to - using collection_select and :owner - which is initialized with @current_user.
In the controller - the code is standard as of now
def create
@issue = Issue.create(issue_params)
respond_to do |format|
if @issue.save!
format.html { redirect_to @issue, notice: 'Issue was successfully created.' }
format.json { render :show, status: :created, location: @issue }
else
format.html { render :new }
format.json { render json: @issue.errors, status: :unprocessable_entity }
end
end
end
... and
def issue_params
params.require(:issue).permit(:summary,:description,:severity,:priority,:assigned_to, :owner,:resolved_at)
end
As I mentioned earlier, this code works thru the console - adds Issue record without any error. However, when I try to use it thru form submission, I get the following error.
User(#70029712374140) expected, got "1" which is an instance of String(#47333497052640)
I have gone thru the documentation and similar questions on SO but have not been able to find a solution. The :owner field value is passed properly as User object but the :assigned_to field is passed as string. For some reason, the ruby magic does not work for me. I am sure this is something trivial but am not able to find fault since couple of days and hence the question.
Any pointers would be appreciated.
TIA.
I think that your problem is probably related to the names of your foreign keys on an Issue. An Issue belongs_to an :owner and an :assigned_to but in the User model the foreign keys are also :owner and :assigned_to -- this leads me to believe that in your database you have columns named :owner and :assigned_to on your issues table
The way to associate that an Issue object belongs_to a User is to have foreign_keys that are IDs like :owner_id and :assigned_to_id. So change your issues table foreign_keys from :object to :object_id and :assigned_to to :assigned_to_id. Forms are not meant to pass objects in as params, they should be passing in IDs.
Your line:
<%= form.collection_select :assigned_to, @users, :id, :name, control_col: 'col-md-4' , class: 'input-sm' %>
is working as expected - it creates select options for the :assigned_to key where the values are the IDs of your users and the labels are the names. As mentioned, I would change your database to have a :assigned_to_id on your Issue object and then update this line to:
<%= form.collection_select :assigned_to_id, @users, :id, :name, control_col: 'col-md-4' , class: 'input-sm' %>
You would then also have an :owner_id on your Issue object so your hidden_field should change to:
<%= form.hidden_field :owner_id, control_col: ' col-md-4', value: @current_user.id %>
(note that if there is a chance that @current_user could be nil then change out @current_user.id
with @current_user.try(:id)
)
You will need to update your strong_params in your controller to:
def issue_params
params.require(:issue).permit(:summary,:description,:severity,:priority,:assigned_to_id, :owner_id,:resolved_at)
end
And finally if you change those columns to be :owner_id and :assigned_to_id then you need to update your User model to have these relationships:
has_many :issues_owned, :class_name => 'Issue', :foreign_key => 'owner_id'
has_many :issues_assigned, :class_name => 'Issue', :foreign_key => 'assigned_to_id'