Nested attributes with collection_select - Rails 4 - ruby-on-rails

When I press the submit button, it goes to my 'create' action and then saves the main object, but the foreign key is null as it has not been passed via post, here is my code:
Models:
class Ticket < ActiveRecord::Base
belongs_to :system
validates :title, presence: true, length: {minimum: 1, maximum: 60}
accepts_nested_attributes_for :system
end
class System < ActiveRecord::Base
has_many :tickets
validates :name, presence: true, length: {minimum: 1, maximum: 50}
end
Controller:
class TicketsController < ApplicationController
def new
#system = System.actives
#priority = Priority.actives
#ticket = Ticket.new
respond_to :js, :html
end
def create
#ticket = Ticket.new(ticket_params)
#ticket.save
respond_with(#ticket)
end
def ticket_params
params.require(:ticket).permit(:title, :active, systems_attributes: [:id,:name])
end
end
View
<%= form_for #ticket, remote: true do |f| %>
<div class="form-group">
<%= f.label :Título %><br>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :Sistema %><br>
<%= f.fields_for :systems do |fs| %>
<%= fs.collection_select :id,
#system.order(:name),:id,:name, {include_blank: 'Selecione...'},
{class: 'form-control', id: 'system_select'}%>
<% end %>
</div>
<div class="actions">
<%= f.submit "Abrir Chamado", class: "btn btn-info" %>
</div>
<% end %>
So, it saves the ticket object with a title, but without a system_id, any helpful tip would be great here.

There are few mistakes in your code.
Firstly,in your new action,you need to add this line #ticket.build_system
def new
#system = System.actives
#priority = Priority.actives
#ticket = Ticket.new
#ticket.build_system
respond_to :js, :html
end
Your ticket_params method should be like this
def ticket_params
params.require(:ticket).permit(:title, :active, system_id,system_attributes: [:id,:name])
end
Notice system_id and system_attributes.
And finally,you need to change this line <%= f.fields_for :systems do |fs| %> to <%= f.fields_for :system do |fs| %>

I don't entirely understand your form, but in ticket_params it looks like you have the :id you want in the array value for systems_attributes. If that is indeed what you're trying to pass your new Ticket, then it needs to be passed differently within Ticket.new. The arguments/options hash you're passing to Ticket.new with ticket_params should be key-value pairs like so:
Ticket.new(title: "yourtitle", other: "whatever", system_id: "your_id")
I'd either change the output of ticket_params so that it passes your system_id in this way, or manually pass it in (keying it out of the parameters) within your create action.

Related

Rails 6: Unpermitted Parameter (many-to-many)

RAILS 6
Hey, I'm working on a class system that uses units and assignments as a many-to-many relationship. When I submit a new assignment form with a dropdown collection for units, the unit is not being received by the controller, but no error log is displayed. When I use byebug, the following error is displayed:
Unpermitted parameter: :unit_ids
Even though it has been permitted. Here's my controller for assignments.
class AssignmentsController < ApplicationController
def new
#assignment = Assignment.new
end
def create
debugger
#assignment = Assignment.new(assignment_params)
#assignment.save
if #assignment.save
flash[:success] = "The unit was successfully submitted."
redirect_to units_path
else
render 'new'
end
end
def show
end
private
def assignment_params
params.require(:assignment).permit(:name, :description, :duedate, user_ids: [])
end
end
Using byebug, I know the unit_id is being correctly received, from this form:
<%= form_for(#assignment, :html => {class: "form-horizontal", role: "form"}) do |f| %>
<div class="form-group">
<div>
<%= f.collection_select(:unit_ids, Unit.all, :id, :name, placeholder: "Units" )%>
</div>
<div>
<%= f.text_field :name, class:"form-control", placeholder: "Title of Assignment", autofocus: true %>
</div>
<div>
<%= f.text_area :description, class:"form-control materialize-textarea", placeholder: "Assignment Description", autofocus: true %>
</div>
<div>
<%= f.text_field :duedate, class: "datepicker", placeholder: "Due Date"%>
</div>
<div class="form-group" id="submitbutton">
<div align = "center">
<%= f.submit class: "btn waves-effect waves-light" %>
</div>
</div>
</div>
<%end%>
Here are the relevant models just to be safe. Note that I added the nested lines to both after I received this error because I saw it on another thread, but it doesn't seem to be fixing it.
class Unit < ApplicationRecord
has_and_belongs_to_many :users
has_and_belongs_to_many :assignments
accepts_nested_attributes_for :assignments
end
And the Assignment model:
class Assignment < ApplicationRecord
has_and_belongs_to_many :units
has_many :users, :through => :units
accepts_nested_attributes_for :units
end
The answer was a mix of a couple things, as Rockwell pointed out I was using User instead of Units, but that still didn't fix it. My collection had multiple choices set to false, so my controller wanted simply
params.require(:assignment).permit(:name, :description, :duedate, :unit_ids)
However, when I set multiple to true, that didn't work. Then, it wanted
params.require(:assignment).permit(:name, :description, :duedate, unit_ids[])
My solution was to leave multiple as true, and use the unit_ids[].
You have to update the permitted parameters
def assignment_params
params.require(:assignment).permit(:name, :description, :duedate, user_ids: [], unit_ids: [])
end
You mentioned that is was permitted, but I do not see unit_ids in the permitted params, I do see user_ids. Is there a spelling error? Or do you just need to include the unit_ids in there?
unit_ids is not a column name. You can use accept_nested_attribute or form object to solve this problem.

Rails where with multiple conditions

In my application I have multiple user roles defined using an enum:
enum role: { staff: 0, clinician: 1, admin: 2 }
Staff users each belong to a university:
Staff Concern:
require 'active_support/concern'
module StaffUser
extend ActiveSupport::Concern
included do
belongs_to :university
has_many :patients
has_many :referral_requests
validates :university_id, presence: true, if: :staff?
end
University Model
class University < ApplicationRecord
has_many :staffs, -> { where role: :staff}, class_name: "User"
has_many :clinicians, through: :lists
has_many :whitelists
belongs_to :market
validates :market_id, presence: true
end
I have a dropdown select menu for Staff Doctor on a patients/new view where I want to display a list of staff users who belong to the same university as the current user, but I can't seem to get it to work. Currently, the dropdown only contains the prompt text. What am I doing wrong?
patients/new view:
<%= form_for(#patient) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="checkbox">
<h1>Tell us about your patient</h1>
<h2>Insurance</h2>
<% Insurance.all.each do |insurance| %>
<%= check_box_tag "patient[insurance_ids][]", insurance.id, #patient.insurance_ids.include?(insurance.id), id: dom_id(insurance) %>
<%= label_tag dom_id(insurance), insurance.name %><br>
<% end %>
<h2>Presenting Concerns</h2>
<% Concern.all.each do |concern| %>
<%= check_box_tag "patient[concern_ids][]", concern.id, #patient.concern_ids.include?(concern.id), id: dom_id(concern) %>
<%= label_tag dom_id(concern), concern.name %><br>
<% end %>
<h2>Staff Doctor</h2>
<%= select_tag "patient[staff_doctor_id]", options_from_collection_for_select(User.where("role = ? AND university_id = ?", "staff", #user.university_id), "id", "name"), prompt: "Select this patient's therapist" %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %
Patients Controller:
class PatientsController < ApplicationController
before_action :require_login
def new
#user = current_user
#patient = current_user.patients.build
end
def index
authorize Patient
#patients = policy_scope(Patient)
end
def show
#patient = Patient.find(params[:id])
end
def edit
#patient = Patient.find(params[:id])
end
def update
#patients = Patient.all
#patient = Patient.find(params[:id])
if #patient.update_attributes(patient_params)
flash[:success] = "Patient Updated!"
render 'patients/index'
else
render "edit"
end
end
def create
#patient = current_user.patients.build(patient_params)
if #patient.save
flash[:success] = "Patient Created!"
redirect_to new_referral_request_path(patient_id: #patient.id)
else
Rails.logger.info(#patient.errors.inspect)
render 'patients/new'
end
end
private
def patient_params
params.require(:patient).permit(:age, :staff_doctor_id, :user_id, insurance_ids: [], gender_ids: [], concern_ids: [], race_ids: [])
end
end
Scopes in ActiveRecord are chainable:
User.staff.where(university: #user.university)
Chaining .where or scopes creates AND clauses. So all the conditions must apply.
Using ActiveRecord::Enum creates scopes for each of the enum states. So this is equivilent to:
User.where(role: :staff, university: #user.university)
When using an ActiveRecord::Enum you need to remember that the database stores integers - not strings:
User.where('role = 0') # staff
User.where('role = ?', User.statuses[:staff])
But there is no need to use a SQL string for this query.
A much better way to create selects and checkboxes is by using the rails collection helpers:
<%= form_for(#patient) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="checkbox">
<h1>Tell us about your patient</h1>
<h2>Insurance</h2>
<%= f.collection_check_boxes(:insurance_ids, Insurance.all, :id, :name) %>
<h2>Presenting Concerns</h2>
<%= f.collection_check_boxes(:concern_ids, Concern.all, :id, :name) %>
<h2>Staff Doctor</h2>
<%= f.collection_select(:staff_doctor_id, User.staff.where(university: #user.university), :id, :name, prompt: "Select this patient's therapist") %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
Not only is this a lot less code, but binding the inputs to the form builder ensures that they "hold the value" when validations fail.

Defining an object in another model in Rails

I have migrated the :bank_name and :bank_account objects in User model.
I want two objects can be define from the Listings model in the listings/view to the User model columns.
I have already done (belongs_to, has_many)relations between two models.
But when I filled the bank_name and bank_account text_fields in Listing/view, I get the following error:
undefined method `bank_name' for #Listing:400123298
Here is my listing/view code:
<%= form_for(#listing, :html => { :multipart => true }) do |f| %>
...
<div class="form-group">
<%= f.label :bank_name %><br>
<%= f.text_field :bank_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :bank_account %><br>
<%= f.text_field :bank_account, class: "form-control" %>
</div>
</end>
listing/controller:
def new
#listing = Listing.new
end
def create
#listing = Listing.new(listing_params)
#listing.user_id = current_user.id
#listing.user_id = User.bank_name.build(params[:bank_name])
#listing.user_id = User.bank_account.build(params[:bank_account])
end
Several issues for you
Nested
As mentioned in the comments, what you're looking at is a nested model structure.
Simply, this means you'll be able to create an associative model from your "parent" - giving you the ability to define the attributes you need in your "parent" model, passing them through to the nested. This functionality is handled by accepts_nested_attributes_for in your parent model
The best resource you can use is this Railscast (only the start):
--
Fix
Here's how you can fix the problem:
#app/models/listing.rb
class Listing < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
end
#app/models/user.rb
class User < ActiveRecord::Base
has_one :bank_account
accepts_nested_attributes_for :bank_account
end
#app/models/bank_account.rb
class BankAccount < ActiveRecord::Base
belongs_to :user
end
#app/controllers/listings_controller.rb
class ListingsController < ApplicationController
def new
#listing = current_user.listings.new
#listing.user.build_bank_account
end
def create
#listing = Listing.new listing_params
#listing.save
end
private
def listing_params
params.require(:listing).permit(:listing, :params, user_attributes: [ bank_account_attributes: [] ])
end
end
This will help you do the following:
#app/views/listings/new.html.erb
<%= form_for #listing do |f| %>
...
<%= f.fields_for :user do |u| %>
<%= u.fields_for :bank_account do |b| %>
<%= b.text_field :name %>
<%= b.text_field :number %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
There is a slight twist to this tail, in that I'm not sure whether your passing of attributes through to your User model. This would be okay if the user was being created at the same time as your other attributes, but as it isn't, we may need to refactor the process of passing the nested data through
If this does not work, please comment & we can work to fix it!

Nested form can't mass assign, create action

I had to add the pen attributes to the paper model to stop the error "can't mass assign :pen", even thought I had the attr_accessible for pen_attributes.
Now, I'm getting a "unknown attribute: pen" error. It's pointing me to the second line of the create action. I can't figure it out.
I basically want to have to have the Paper New action create the pen and assign it to the paper.
Paper model
attr_accessible :name, :size, :line,
:pen_attributes,
:pen, :colour, :style
has_many :pens
accepts_nested_attributes_for :pens
Pens model
attr_accessible :name, :size, :line, :paper_attributes, :paper_id
belongs_to :paper
<%= simple_nested_form_for #paper do |f| %>
<%= f.input :name %>
<%= f.input :size, :placeholder => "text" %>
<%= f.input :line %>
<%= f.fields_for #pen do |h| %>
<%= h.input :pen, %>
<%= h.input :colour %>
<%= h.button :submit, :label => "create" %>
<% end %>
<% end %>
Paper Controller
def new
#user = current_user
#paper = #user.paper.build(params[:paper])
#pen = Pen.new(params[:pen])
end
def create
#user = current_user
#paper = #user.papers.build(params[:paper])
#pen = #paper.pens.build(params[:pen])
if #paper.save
flash[:notice] = "#{#paper.name} Created"
redirect_to(:action => "index")
else
flash.now[:notice] = "Error"
render 'new'
end
end
{"utf8"=>"✓",
"authenticity_token"=>"Z8vncB9ewDM1bWiKfsPHOGlkxcGpfhPjv0xpamudIIs=",
"paper"=>{"name"=>"three",
"size"=>"three",
"colour"=>"red",
"pen"=>{"colour"=>"test",
"pen"=>"test"}},
"commit"=>"Create"}
It looks like you have some minor discrepancies in your singular/plural naming.
I think you need to adjust the following:
<%= f.fields_for :pens, #pen do |h| %>
and probably:
attr_accessible :pens_attributes
as well as (possibly):
params[:pens]
Hope this helps, good luck!

Nested model forms doesnt work on rails

I'm really new to both ruby on rails and programming. I am trying to develop an application but i am stucked now. I was watching http://railscasts.com/episodes/196-nested-model-form-part-1 to make nested model forms but i am having an error. My problem details are as follows;
I have employers model, and employers model has_many interviews, and interview model has_many customquestions. I'm trying to create a form through which i will collect info to create interview. Although i made all necessary assosications, when i submit the form it raises error saying that "Customquestions interview can't be blank". I am kinda sure that it is because of that i miss some code in interview controller. Below you can see my interview controller and the form template that i am using to submit info.
Interview Controller
class InterviewsController < ApplicationController
before_filter :signed_in_employer
def create
#interview = current_employer.interviews.build(params[:interview])
if #interview.save
flash[:success] = "Interview created!"
redirect_to #interview
else
render 'new'
end
end
def destroy
end
def show
#interview = Interview.find(params[:id])
end
def new
#interview = Interview.new
3.times do
customquestion = #interview.customquestions.build
end
end
end
Form which i use to submit info:
<%= provide(:title, 'Create a new interview') %>
<h1>Create New Interview</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#interview) do |f| %>
<%= render 'shared/error_messages_interviews' %>
<%= f.label :title, "Tıtle for Interview" %>
<%= f.text_field :title %>
<%= f.label :welcome_message, "Welcome Message for Candidates" %>
<%= f.text_area :welcome_message, rows: 3 %>
<%= f.fields_for :customquestions do |builder| %>
<%= builder.label :content, "Question" %><br />
<%= builder.text_area :content, :rows => 3 %>
<% end %>
<%= f.submit "Create Interview", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
In interview model, i used accepts_nested_attributes_for :customquestions
Interview Model
class Interview < ActiveRecord::Base
attr_accessible :title, :welcome_message, :customquestions_attributes
belongs_to :employer
has_many :customquestions
accepts_nested_attributes_for :customquestions
validates :title, presence: true, length: { maximum: 150 }
validates :welcome_message, presence: true, length: { maximum: 600 }
validates :employer_id, presence: true
default_scope order: 'interviews.created_at DESC'
end
The validation error gets raised in the customquestions model because (I assume) it validates :interview_id. The problem is that interview_id won't get set until the parent object (Interview) is saved, but validations for customquestion are run before Interview is saved.
You can let cusomtquestions know about this dependency by adding the option :inverse_of=> :customquestions to belongs_to :interview in the customquestions model.

Resources