I am trying to use JSONB in a deeply tested form that works
<%= simple_form_for(#company, local: true) do |form| %>
<%= form.simple_fields_for #schedule do |ff| %>
<%= ff.select :status, Schedule.statuses.keys.collect {
|status| [Schedule.human_enum_name(:status, status), status] },
class:"custom-control", label: false %>
<%= ff.date_field :valid_from %>
<%= ff.date_field :valid_through %>
<%= ff.simple_fields_for :business_hours do |field| %>
<% I18n.t('date.day_names').each_with_index do |day, wday| %>
<%= field.label :"#{day.downcase}" %></td>
<%= field.select :"#{day.downcase}_closes_at",
collection: time_select_options,
class: 'custom-select',
include_blank: "Closed" %>
<%= field.select :"#{day.downcase}_closes_at",
collection: time_select_options,
class: 'custom-select',
include_blank: "Closed" %>
<% end %>
<% end %>
<% end %>
The nested business_hours field is the JSONB col.
The Schedule model belongs_to Company, which has_may Schedules.I am using store_accessor :business_hours in the Schedule model.
Schedule model looks like:
has_many :schedules, inverse_of: :company, dependent: :destroy
accepts_nested_attributes_for :schedules, allow_destroy: true,
reject_if: proc { |att| att["day"].blank? }
the Company controller is set as follows:
def new
#account = current_account
#company = #account.company.new
#chedule = #company.schedules.new
end
def create
#account = current_account
#company = #account.build_company(company_params)
#company.save
end
def edit
#schedules = #company.schedules.all
end
def update
#company.update(company_params)
end
i am whitelisting with
def company_params
params.require(:company).permit(:name, ...,
...,
schedules_attributes: [:status, :id, :_destroy, :day, :valid_from, :valid_through,
business_hours: [:sunday_opens_at, ...]
end
the form submit as follows:
Processing by CompaniesController#update as HTML
Parameters: {"authenticity_token"=>"...", "company"=>{"schedule"=>{"status"=>"active", "valid_from"=>"2020-06-01", "valid_through"=>"2020-06-01", "business_hours"=>{"monday_closes_at"=>"00:00:00", "tuesday_closes_at"=>"00:00:00", ...}}}, "button"=>"", "locale"=>"en", "id"=>"acme-sas"}
however I receive: Unpermitted parameter: :schedule
you need to send schedule as schedules
Processing by CompaniesController#update as HTMLParameters: {"authenticity_token"=>"...", "company"=>{"schedules"=>{"status"=>"active", "valid_from"=>"2020-06-01", "valid_through"=>"2020-06-01", "business_hours"=>{"monday_closes_at"=>"00:00:00", "tuesday_closes_at"=>"00:00:00", ...}}}, "button"=>"", "locale"=>"en", "id"=>"acme-sas"}
Related
I have two tables class_details and user_classes. user_classes table is dependent of user table and class_details and class_details is independent of user table. Now my requirement is that when i click a button few details must be saved to the database which belong to different tables. User dependent table are only getting saved to database and not the user independent tables and i am getting error undefined method class_detail for nil:NilClass
Controller code
def update_profile
if #user.update(user_params)
redirect_to profile_index_path, notice: 'Profile was successfully updated.'
else
render :index
end
end
end
private
def set_user
#user = User.find(current_user.id)
#user.fee || #user.build_fee
#user.user_class || #user.build_user_class
end
def user_params
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: %i[id class_detail [id class_name class_category]])
end
class_detail.rb
class ClassDetail < ApplicationRecord
has_one :user_class, dependent: :destroy
end
user_class.rb
class UserClass < ApplicationRecord
belongs_to :user
belongs_to :class_details
validates_presence_of :user_id
end
user.rb
has_one :fee, dependent: :destroy
accepts_nested_attributes_for :fee
has_one :user_class, dependent: :destroy
view code
<%= form_for(#user, url: {action: 'update_profile'}, html: {class: 'm-form m-form--fit m-form--label-align-right'}) do |f| %>
<%= f.fields_for :fee, #user.fee do |u| %>
<%= u.number_field :fee_hour, class: 'form-control m-input', placeholder: t('.fee_for_hour') %>
<% end %>
<%= f.fields_for :user_class, #user.user_class do |k| %>
<%= f.fields_for :class_detail, #user_class.class_detail do |c| %>
<%= c.text_field :class_name, class: 'form-control m-input' %>
<% end %>
<% end %>
Can anyone help me updating user independent table! Thank you in advance
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: %i[id class_detail [id class_name class_category]])
That means you want to update class_detail from user_class, but you need to define nested attributes:
class UserClass < ApplicationRecord
belongs_to :user
belongs_to :class_details
accepts_nested_attributes_for :class_details, update_only: true
validates_presence_of :user_id
end
Also, the form must look like this:
<%= form_for(#user, url: {action: 'update_profile'}, html: {class: 'm-form m-form--fit m-form--label-align-right'}) do |f| %>
<%= f.fields_for :fee, #user.fee do |u| %>
<%= u.number_field :fee_hour, class: 'form-control m-input', placeholder: t('.fee_for_hour') %>
<% end %>
<%= f.fields_for :user_class, #user.user_class do |k| %>
<%= k.fields_for :class_detail, #user.user_class.class_detail do |c| %>
<%= c.text_field :class_name, class: 'form-control m-input' %>
<% end %>
<% end %>
<% end %>
And in your controller:
def user_params
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: [:id, { class_detail: %i[class_name class_category] }])
end
Edited:
That all means class_details and user_class are associated to the user already. Build references model - child-child-model - child-parent-model from the single call not possible. You can build this references in the edit action. For example:
def edit
#user.create_user_class!(class_detail: ClassDetail.find(n)) unless #user.user_class
end
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.
I have a few models in my project : Request, Work, Car and Employee. Work is an intermediate model between Request and Car/Employee.
Here are the associations:
Request
has_many :works, dependent: :destroy
def performers
works.map {|x| x.performer}
end
Work
belongs_to :request
belongs_to :performer, polymorphic: true
Car
has_many :works, as: :performer
has_many :requests, through: :works, as: :performer
Employee
has_many :works, as: :performer
has_many :requests, through: :works, as: :performer
View used to create works:
<%= form_for([#request, #work]) do |f| %>
<%= (f.collection_select :performer_id, Employee.all, :id, :name) if #request.type == "StaffRequest" %>
<%= (f.collection_select :performer_id, Car.all, :id, :select_info) if #request.type == "CarRequest" %>
<%= f.submit 'OK' %>
<% end %>
Work controller
def new
#work = #request.works.new
end
def create
#work = #request.works.new(work_params)
end
def work_params
params.require(:work).permit(:performer_id, :request_id)
end
The problem is that my performer_type column is always empty, it does not save the class name. What can be the problem? Any ideas?
It's empty because you did't pass it, you should add a hidden field for you form:
<%= form_for([#request, #work]) do |f| %>
<% if #request.type == "StaffRequest" %>
<%= (f.hidden_field :performer_type, value: "Employee") %>
<%= (f.collection_select :performer_id, Employee.all, :id, :name) %>
<% elsif #request.type == "CarRequest" %>
<%= (f.hidden_field :performer_type, value: "Car") %>
<%= (f.collection_select :performer_id, Car.all, :id, :select_info) %>
<% end %>
<%= f.submit 'OK' %>
<% end %>
Beside :performer_id, you have to pass the :performer_type also, one way to do this is via the form select_tag :
def create
#work = #request.works.new(work_params)
end
def work_params
# use :performer if you use :performer as in the select form field
params.require(:work).permit(:performer, :request_id)
# OR
# use :performer_id & :performer_type if you also use the hidden field
params.require(:work).permit(:performer_id, :performer_type, :request_id)
end
There is a good example (for Rails 4.2) of using a single select form field for polymorphic so you can write like:
<%= f.grouped_collection_select :global_performer, [ Car, Employee ], :all, :model_name, :to_global_id, :name %>
How to create grouped select box in Rails for polymorphic association using Global ID?
I need to validate whether or not a Credit has been assigned to a Track in a form. The track is saving, the errors.add isn't working.
Ideas?
class Track
has_many :credits
accepts_nested_attributes_for :credits, allow_destroy: true
attr_accessible :credits_attributes
has_many :vendors, through: :credits
accepts_nested_attributes_for :vendors
attr_accessible :vendor_ids
end
And trying to validate that the Credit bit of the form is filled out on a Track edit page:
def verify_credits
#track = Track.find(params[:id])
if !params.has_key?(:split) || params[:track][:credits_attributes][:vendor_id].empty?
puts '*' * 300
#track.errors.add(:base, 'You must credit this track to a Vendor with a split')
end
end
and I call verify_credits in overwritten contoller actions in ActiveAdmin track.rb. The puts '*' is coming through so I know my conditional is fine. Example:
def update
referrer = params[:track][:referrer]
params[:track].delete(:referrer)
s3_path = params[:track][:file_cache] if params[:track][:file_cache]
params[:track].delete(:file_cache) unless s3_path.empty?
#track = Track.find(params[:id])
#track.assign_attributes(params[:exclusivities])
verify_credits
authorize! :update, #track
if s3_path
Delayed::Job.enqueue ProcessTrackFileJob.new(#track.id, s3_path) unless s3_path.empty?
end
super do |format|
format.html { redirect_to referrer } unless referrer.to_s.empty?
end
end
ADDED:
_form.html.erb
<%= f.inputs "Vendors" do %>
<% f.has_many :credits do |c|%>
<% c.input :vendor, as: :string, required: true, input_html: { %>
<% class: 'autocomplete', %>
<% name: '', %>
<% value: c.object.vendor.try(:name), %>
<% data: { %>
<% url: autocomplete_manage_vendors_path %>
<% } %>
<% } %>
<% c.input :vendor_id, as: :hidden %>
<% c.input :_destroy, as: :boolean, required: false, label: "Remove" %>
<% c.input :split %>
<% end %>
<% end %>
<% end %>
You don't have any validations in your Track model. If you're trying to make sure there is a vendor_id, you would need validates :vendor_id, presence: true in your Track model. If not, your Track model doesn't know there is an error if vendor_id is missing.
I'm trying to deal with nested forms in my Rails4 application but I'm having problems with has_one relationship because all the GEM documentation is only for has_many. When I try to create a new examination the app is throwing "Unpermitted parameters: exam_statuses" message. Probably I'm doing something wrong with singular&plural names but can you please check what is wrong with this code. Here is my codes;
examination.rb:
class Examination < ActiveRecord::Base
has_one :exam_status, dependent: :destroy
accepts_nested_attributes_for :exam_status, :reject_if => :all_blank, :allow_destroy => true
end
examination_controller.rb:
.....
def new
#examination = Examination.new
end
def create
#examination = Examination.new(examination_params)
respond_to do |format|
if #examination.save
format.html { redirect_to examinations_path, notice: 'success' }
else
format.html { render action: 'new' }
end
end
end
private
def set_examination
#examination = Examination.find(params[:id])
end
def examination_params
params.require(:examination).permit(:name, :shortname, :fee, :exam_status => [:first_application_date, :last_application_date, :examination_id])
end
views/examinations/new.html.erb =>
<%= simple_form_for(#examination) do |f| %>
<%= f.input :name %>
<%= f.input :shortname %>
<%= f.input :fee %>
<%= f.simple_fields_for :exam_status do |exam_status| %>
<%= render 'exam_status_fields', :f => exam_status %>
<% end %>
<% end %>
views/examinations/_exam_status_fields.html.erb =>
<div class="nested-fields">
<%= f.input :first_application_date, as: :datetime %>
<%= f.input :last_application_date, as: :datetime %>
<%= f.input :examination_id %>
</div>
Log File =>
Started POST "/examinations" for 127.0.0.1 at 2014-07-03 16:43:30 +0300
Processing by ExaminationsController#create as HTML
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"uYpiZ9Z1txaJ1mib+NEAG7Ckwm9F3TyNQ6jRpYz3ncA=",
"examination"=>{"name"=>"nnnnn", "shortname"=>"nnnnnn", "fee"=>"33",
"exam_statuses"=>{"first_application_date(3i)"=>"3", "first_application_date(2i)"=>"7", "first_application_date(1i)"=>"2014", "first_application_date(4i)"=>"16", "first_application_date(5i)"=>"43", "last_application_date(3i)"=>"5", "last_application_date(2i)"=>"7", "last_application_date(1i)"=>"2014", "last_application_date(4i)"=>"16", "last_application_date(5i)"=>"43", "examination_id"=>""}}}
Unpermitted parameters: exam_statuses
You have a few issues here.
1. Controller
In your new method you need to build exam_status for examination:
# GET /examinations/new
def new
#examination = Examination.new
#examination.build_exam_status
end
And in your examination_params method, you need to change it to:
def examination_params
params.require(:examination).permit(:name, :shortname, :fee, exam_status_attributes: [:first_application_date, :last_application_date, :_destroy])
end
Notice that :exam_status needs to be exam_status_attributes and it doesn't need examination_id param (Rails takes care of that). Also note that I added _destroy. This is a flag used to destroy the object.
2. Nested fields partial
Remove examination_id field from the partial.
<div class="nested-fields">
<%= f.input :first_application_date, as: :datetime %>
<%= f.input :last_application_date, as: :datetime %>
</div>
These changes will take care of the issue.