Rails 5 Simpleform not updating nested attributes - ruby-on-rails

Sorry I've looked at similar questions to this but still can't figure it out for some reason...
I'm using Devise and I'm trying to update my User model and a City model at the same time (through the edit registrations page) using a nested form.
I'm updating the users city based on a set of predefined values that are in the database. So all the user has to do is select a city they are living in from a list and then it save.
All that happens at the moment is the form submits but the city isn't changed.
Thanks in advance!
#user.rb
class User < ApplicationRecord
belongs_to :city
end
#city.rb
class City < ApplicationRecord
has_many :users
end
My Controller:
class UsersController < ApplicationController
def index
#users = User.all
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(city_attributes: [:id, :name, :user_id])
end
end
And my form:
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= f.simple_fields_for :city do |c| %>
<%= c.input :city_id, collection: City.all.order(name: :asc), label_method: :name, value_method: :id, label: "City", include_blank: false, selected: #user.city_id %>
<% end %>
<% end %>

Okay so I sorted my problem out based on the comment from Iceman and response from ajerferson.
(However the issue that the nested form doesnt work still remains, but my issue is fixed)
As Iceman said Devise was permitting its own parameters in my application controller. So I moved the parameters there (instead of changing my routes).
# application_controller.rb
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name])
devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :last_name, :date_of_birth, :gender, city_attributes: [:id, :name, :user_id]])
end
I tried again but the same problems were occuring. So what I did was actually scrap the nested form and simply change my application controller to this and targetting the city_id foreign key:
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name])
devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :last_name, :date_of_birth, :gender, :city_id])
end
My form:
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= f.input :city_id, collection: City.all.order(name: :asc), label: "City" %>
<%= f.button :submit, "Save" %>
<% end %>
and now it works perfectly fine!

Try this out:
def user_params
params.require(:user).permit(:city_id)
end

Related

Rails Strong Params Issue With Nested Models

I am trying to render a new view on an already existing user show page. When trying to submit this view, I get param is missing or the value is empty: user. To be clear this is a skill partial being rendered on the user show page. For some reason it is using the strong params in my User Controller.
The code:
show.html.erb for user
<h4>Create a Skill</h4>
<%= render partial: "skills/form" %>
userscontroller.rb
def show
#user = User.find(params[:id])
#skill = Skill.new
#skills = #user.skills.all
end
private
def user_params
params.require(:user).permit(:username, :password, :avatar_url, :email, :about, :cover_letter, :city, :state)
end
end
SkillsController.rb
class SkillsController < ActionController::Base
def new
user = User.find(params[:user_id])
#skill = user.skills.new
end
def create
user = User.find(params[:user_id])
#skill = user.skills.new(skill_params)
if #skill.save
flash[:message] = "#{#skill.name} skill has been created!"
redirect_to user_path(user)
else
redirect_to new_user_skill_path
end
end
private
def skill_params
params.require(:skill).permit(:name, :level)
end
end
Also, I have Namespaced skills within user. No authentication in place yet.
EDIT: #nickm, here are the contents of skills/_form
<%= simple_form_for(Skill.new, :url => { :action => "create" }) do |f| %>
<%= f.input :name, label: 'Skill Name ' %>
<%= f.input :level, label: "Skill Level ", collection: ["Beginner","Proficient", "Intermediate", "Advanced", "Expert"], include_blank: false, include_hidden: false %>
<%= f.submit %>
<% end %>
The problem is that you aren't passing a user_id through the form. You would have to either add a form input:
<%= f.hidden_field :user_id, some_value %>
Then find the user:
user = User.find(params[:skill][:user_id])
and then make skill_params
def skill_params
params.require(:skill).permit(:name, :level, user_id)
end
Or optionally, set the value of user_id in your controller action. Not sure how you're going to pass that value since you haven't built any authentication yet. If you were using something like devise you could do
current_user.skills.new(skills_params)
...in your create action.

Rails 5. Association one to one, updating record create new one

Model User:
class User < ApplicationRecord
has_one :address, foreign_key: :user_id
accepts_nested_attributes_for :address
end
Model Address
class Address < ApplicationRecord
belongs_to :user, optional: true
end
Controller User, everything happen here
class UsersController < ApplicationController
def home # method which I use to display form
#user = User.find_by :id => session[:id]
end
def update # method for updating data
#user = User.find(session[:id])
if #user.update(user_params)
flash[:notice] = "Update successfully"
redirect_to home_path
else
flash[:error] = "Can not update"
redirect_to home_path
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, images_attributes: [:image_link, :image_description], address_attributes: [:city, :street, :home_number, :post_code, :country])
end
end
Updating form:
<%= form_for #user, :html => { :id => "update-form", :class => "update-form"} do |f| %>
<%= f.text_field :name %>
<%= f.text_field :email %>
<%= f.fields_for :address do |a| %>
<%= a.text_field :city %>
<%= a.text_field :street %>
<%= a.number_field :home_number %>
<%= a.text_field :post_code %>
<%= a.text_field :country %>
<% end %>
<%= f.submit %>
<% end %>
When I submitting my form, it shows me everything is fine, I mean "Update successfully", but in database its looks like new record is added to address table, but user table is updated properly. Can someone give me explanation why? I am looking answers in google but nothing helps me.
When I submitting my form, it shows me everything is fine, I mean
"Update successfully", but in database its looks like new record is
added to address table, but user table is updated properly. Can
someone give me explanation why?
This is due to the nature of strong params. It expects :id to be permitted for the nested_attributes to update properly, else a new record is created instead. Permit the :id and you are good to go.
def user_params
params.require(:user).permit(:name, :email, :password, images_attributes: [:id, :image_link, :image_description], address_attributes: [:id, :city, :street, :home_number, :post_code, :country])
end
try below code in your controller:
class UsersController < ApplicationController
def home # method which I use to display form
#user = User.find_by :id => session[:id]
end
def update # method for updating data
#user = User.find(session[:id])
if #user.update(user_params)
flash[:notice] = "Update successfully"
redirect_to home_path
else
flash[:error] = "Can not update"
redirect_to home_path
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, images_attributes: [:image_link, :image_description], address_attributes: [:id, :city, :street, :home_number, :post_code, :country])
end
end

how to permit parameters for profile creation along with devise user signup

I have the classic has_one/belongs_to association between user and profile models, and user accepts_nested_attributes for profile. In the signup form, one of the inputs establishes the username attribute of the profile model. This attribute is correctly saved, but the rails server spits out "Unpermitted parameter: profile_attributes". Even though there doesn't seem to be any actual errors in the result, I'd like to know why I'm getting this message and eliminate it.
My registrations_controller.rb
def new
build_resource({})
resource.build_profile
set_minimum_password_length
yield resource if block_given?
respond_with self.resource
end
def sign_up_params
devise_parameter_sanitizer.sanitize(:sign_up)
params.require(:user).permit(:email, :password, :password_confirmation, profile_attributes: [:username])
end
They relevant part of my form code (if it matters at all) is:
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= f.input :email, required: true, autofocus: true %>
<%= f.simple_fields_for :profile do |p| %>
<%= p.input :username, required: true %>
<% end %>
...
I tried adding profile_attributes: [:username] to params.require in users_controller.rb and that didn't eliminate the error message either.

Update child object in nested form in Rails 4

My current devise config has a user object that has a single table inheritance structure that breaks down into two further user types (one of them is business). The child object of business I am trying to update is called 'supp_forms'. When I try and update the record I get the following error in terminal. I am using the nested_form_for gem to handle my nested forms.
Unpermitted parameters: supp_form_attributes
However, the parameters being passed through look correct (the data being passed through is the data that I have edited in the form).
Parameters: {"utf8"=>"✓", "authenticity_token"=>"XX", "business"=>{"supp_form_attributes"=>{"work_phone_number"=>"(906) 790-6969 x69696", "business_address"=>"1 XXXX st", "business_postal_code"=>"L0R 1K2", "business_city"=>"Oria", "business_province"=>"ON", "employee_count"=>"5", "id"=>"96"}}, "commit"=>"Update Business"}
My update form looks like the following.
business_profile.html.erb
<%= nested_form_for #user, url: business_registration_path do |f| %>
<%= f.fields_for :supp_form do |supp_form| %>
<%= supp_form.label :work_phone_number %>
<%= supp_form.text_field :work_phone_number %>
<%= supp_form.label :business_address %>
<%= supp_form.text_field :business_address %>
<%= supp_form.label :business_postal_code %>
<%= supp_form.text_field :business_postal_code %>
<%= supp_form.label :business_city %>
<%= supp_form.text_field :business_city %>
<%= supp_form.label :business_province %>
<%= supp_form.text_field :business_province %>
<%= supp_form.label :employee_count %>
<%= supp_form.text_field :employee_count %>
<% end %>
<%= f.submit %>
<% end %>
business.rb
class Business < User
# Associations
has_one :supp_form
has_many :loan_applications
has_many :transactions
has_many :listing_information_forms
# Nested attributes
accepts_nested_attributes_for :supp_form
end
supp_form.rb
class SuppForm < ActiveRecord::Base
# Associations
belongs_to :business
end
supp_forms_controller.rb
class SuppFormsController < ApplicationController
before_filter :authenticate_user!
def edit
#user = User.current_user
end
def update
#user = current_user
#suppform = #user.supp_form
if #suppform.update_attributes(supp_form_params)
business_supp_form_path(#user)
else
render 'edit'
end
end
private
def supp_form_params
params.require(:supp_form).permit(:business_id, :title, :loan_agreement_authorization, :first_name, :last_name, :applicant_role, :work_phone_number, :business_address, :business_postal_code,:business_city, :business_name, :years_in_business, :legal_structure, :ownership, :business_industry, :employee_count, :mobile_phone_number, :business_province, :business_country)
end
end
business_account_controller.rb
class BusinessAccountController < ApplicationController
before_filter :authenticate_user!
def business_profile
#user = current_user
end
end
registrations_controller.rb (for businesses)
class Businesses::RegistrationsController < Devise::RegistrationsController
before_filter :update_sanitized_params
def edit
#user = current_user
super
end
def update
#user = current_user
super
end
private
def update_sanitized_params
devise_parameter_sanitizer.for(:sign_up) {|u| u.permit(:email, :password, :password_confirmation, :type, :confirmed_at, :business_name, :terms, :railsid, :terms_of_service, supp_form_attributes: [:business_id, :title, :loan_agreement_authorization, :first_name, :last_name, :work_phone_number, :applicant_role, :business_address, :business_postal_code, :business_city, :business_name, :years_in_business, :legal_structure, :ownership, :business_industry, :employee_count, :mobile_phone_number, :business_province, :business_country])}
end
end
Please try to do as following, it works on my side and hope it helpful for you.
class RegistrationsController < Devise::RegistrationsController
def create
devise_parameter_sanitizer.for(:sign_up) << { profile_attributes: [:first_name, :last_name] }
super
end
end

ActiveModel::ForbiddenAttributesError using update_attributes having created params hash myself

I’m trying to edit/update a model record using simple_form, but the form is not going to directly change a model field. Instead, I offer a couple of check_box_tag fields that tell update what fields need changed. As a result, update is not receiving a params[:device] hash that I can use to update the attributes. I am attempting to create this hash, but am getting ForbiddenAttributesError when I issue the #device.update_attributes(params[:device]).
I believe my strong parameters list is correct. If I allow one model field (name) to be processed in the edit view, I receive the expected params[:device] hash and everything works. If I disable that field, because I don’t want it to be changed, then I need to create that hash myself and I receive the error. When I look at the hash I created, it looks to me as equivalent to the one passed by the view. I don’t understand why it is failing.
Environment is Ruby 2.0.0, Rails 4.1 on Windows 8.1 with RubyMine 6.3.
The form is: <... needs correct formatting once working ...>
<%= simple_form_for #device do |f| %>
<legend><%= controller.action_name.capitalize %> Device:</legend>
<%= f.input :name, disabled: true %>
<%= check_box_tag(:is_admin, 0, #device.admin?) %>
<%= label_tag(:is_admin, "Make admin?") %>
<%= check_box_tag(:chg_pwd) %>
<%= label_tag(:chg_pwd, "Change password?") %>
<%= f.button :submit %>
<% end %>
The params[:device] that I receive when I sent f.input :name, disabled: false and allow the view to generate params[:device] is:
ActionController::Parameters (3 element(s))
"{"name"=>"D105", "password"=>"D105Dvgr", "password_confirmation"=>"D105Dvgr"}"
And, everything works.
The params[:device] that I create is:
ActionController::Parameters (3 element(s))
"{"name"=>"D106", "password"=>"D106VdAd", "password_confirmation"=>"D106VdAd"}"
And, I receive Forbidden Attribute Error, even though I see no difference between the two.
The update is: <... Code needs refactored, once it is working...>
class DevicesController < ApplicationController
before_filter :authenticate_device!
... other methods removed here ...
def edit
#device = Device.find(params[:id])
# my_page = render_to_string controller: 'devices', action: 'edit', layout: "application"
end
def update
authorize! :update, #device, :message => 'Not authorized as an administrator.'
#device = Device.find(params[:id])
pwd_msg = ""
if params[:chg_pwd]
pwd_gen = #device.device + SecureRandom.urlsafe_base64(15).tr('lIO0=_\-', 'sxyzEUM').first(4)
params[:device] = {name: #device.name} if params[:device].nil?
params[:device][:password] = pwd_gen
params[:device][:password_confirmation] = pwd_gen
pwd_msg = ", new password is #{pwd_gen}"
end
if #device.update_attributes(params[:device])
params[:is_admin] ? #device.add_role(:admin) : #device.remove_role(:admin)
flash[:notice] = ["Device updated" + pwd_msg]
redirect_to devices_path
else
#device.errors.messages.each do |key, value|
flash[:alert] = ["Unable to update device"]
#device.errors.messages.each do |key, value|
flash[:alert] << key.to_s.capitalize + " " + value[0]
end
end
redirect_to devices_path
end
end
private
def device_params
params.require(:device).permit(:device, :name, :email, :password, :password_confirmation, :encrypted_password, :salt, :role_ids, :is_admin, :chg_pwd) # TODO minimize when update is working
end
end
The model is:
class Device < ActiveRecord::Base
rolify
devise :database_authenticatable, :rememberable, :trackable, :validatable
validates :device,
presence: true,
length: {minimum: 4 },
uniqueness: {case_sensitive: false }
validates :name,
presence: true
def remember_me
true unless self.admin?
end
def admin
self.add_role :admin
end
def not_admin
self.remove_role :admin
end
def admin?
self.has_role? :admin
end
def device?
self.has_role? :device
end
def vip?
self.has_role? :vip
end
def login=(login)
#login = login
end
def login
#login || self.device || self.email
end
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login) # Note one equal sign. Strange but true.
where(conditions).where(["lower(device) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
end
NEW INFORMATION: I neglected to provide information I have in the ApplicationController. This fix from Anton Trapp handles strong parameters for gems that aren't yet fully Rails 4 compatible:
before_filter do
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
I have found that using the proposed solution of:
#device.update_attributes(device_params)
does not work if a model field is updated. The result is "param not found: device". It does work if no model field is update. So, the whole issue begs the question of what is truly wrong.
In DevicesController#update action, change
#device.update_attributes(params[:device])
To
#device.update_attributes(device_params)
As you are using Rails 4.1, you need to whitelist the attributes which you would like to be inserted/updated in database. As you passed the attributes directly to update_attributes method without permitting them you received ActiveModel::ForbiddenAttributesError
UPDATE
To resolve param not found: device:
def device_params
if params[:device]
params.require(:device).permit(:device, :name, :email, :password, :password_confirmation, :encrypted_password, :salt, :role_ids, :is_admin, :chg_pwd) # TODO minimize when update is working
end
end
The fix was to add the fields as attr_accessor to the model, but not the database, so that it could be used correctly within the form.
attr_accessor :is_admin, :chg_pwd
And then modify the view to:
<%= simple_form_for #device do |f| %>
<legend><%= controller.action_name.capitalize %> Device:</legend>
<%= f.input :name, disabled: true %>
<%= f.input :is_admin, as: :boolean, checked_value: true, unchecked_value: false %>
<%= f.input :chg_pwd, as: :boolean, checked_value: true, unchecked_value: false %>
<%= f.button :submit %>
<% end %>
Then, due to the Application Controller code from Anton Trapp:
before_filter do
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
I was able to update the fields in Device Controller as follows:
#device.update_attributes(params[:device])

Resources