ActiveModel::ForbiddenAttributesError in PlayersController#create - ruby-on-rails

I am creating first rails app and had everything working yesterday. I created association on my view today for players to pull to team page with has_many and belongs_to. Now I can not create a new player as it keeps giving me an ActiveModel::ForbiddenAttributesError in PlayersController#create message.
Extracted source (around line #27):
def create
#player = Player.create(params[:player])
respond_to do |format|
if #player.save
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"uw5w2sOgNF6y3+Jv6kvTj3X/dV2+PAVo2/OyHinIirY=",
"player"=>{"first_name"=>"test",
"last_name"=>"",
"address_1"=>"",
"address_2"=>"",
"city"=>"",
"state"=>"",
"zip"=>"",
"phone_number"=>"",
"email"=>"",
"birthday"=>"",
"position"=>"",
"bio"=>"",
"team"=>"",
"team_id"=>"1",
"number"=>""},
"commit"=>"Create Player"}
My players controller for create is:
def create
#player = Player.new(player_params)
respond_to do |format|
if #player.save
format.html { redirect_to #player, notice: 'Player was successfully created.' }
format.json { render action: 'show', status: :created, location: #player }
else
format.html { render action: 'new' }
format.json { render json: #player.errors, status: :unprocessable_entity }
end
end
end

You are supposed to have this player_params method in your controller, and use it to pass the params to your model actions (create, update)
class PlayersController
...
def create
#player = Player.create(player_params)
...
end
private
def player_params
allow = [:first_name, :last_name, :address_1, :address_2, :city, :state, :zip, :phone_number, :email, :birthday, :position, :bio, :team, :team_id, :number]
params.require(:player).permit(allow)
end
end

I've been going crazy searching for a solution for this, when I already had applied the params.require bit. So for people who are using cancan, you need to add this part to the ApplicationController:
See: https://github.com/ryanb/cancan/issues/571
before_filter do
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end

class PlayersController
...
def create
#player = Player.new(player_params)
...
end
private
def player_params
params.require(:player).permit(:first_name, :last_name, :address_1, :address_2, :city, :state, :zip, :phone_number, :email, :birthday, :position, :bio, :team, :team_id, :number)
end
end

If you're using Rails 4 and you've added player_params method, don't forget to change params[:player] in your create method to just player_params.

Related

Create action - ActiveModel::ForbiddenAttributesError

I'm trying to create clinics associated to the current user. But when I hit submit, I get this error.
I'm using devise, rails admin and cancancan. I don't know if any of these could be causing the error, or something else is wrong.
ActiveModel::ForbiddenAttributesError in ClinicsController#create
ActiveModel::ForbiddenAttributesError
user.rb
class User < ApplicationRecord
has_many :clinics, dependent: :destroy
accepts_nested_attributes_for :clinics, reject_if: :all_blank, allow_destroy: true
end
clinic.rb
class Clinic < ApplicationRecord
belongs_to :user
end
clinics_controller.rb
class ClinicsController < ApplicationController
before_action :set_clinic, only: [:show, :edit, :update, :destroy]
def index
#clinic = Clinic.all
#user = current_user
end
def show
#clinic = Clinic.find(params[:id])
#user = current_user
end
def edit
#clinic = Clinic.find(params[:id])
end
def new
#clinic = current_user.clinics.new
end
def create
#clinic = current_user.clinics.new(params[:clinic])
respond_to do |format|
if #clinic.save
format.html { redirect_to #clinic, notice: 'Clinic was successfully created.' }
format.json { render :show, status: :created, location: #clinic }
else
format.html { render :new }
format.json { render json: #clinic.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #clinic.update(params[:clinic])
format.html { redirect_to #clinic, notice: 'Clinic was successfully updated.' }
format.json { render :show, status: :ok, location: #clinic }
else
format.html { render :edit }
format.json { render json: #clinic.errors, status: :unprocessable_entity }
end
end
end
def destroy
#clinic.destroy
respond_to do |format|
format.html { redirect_to clinics_url, notice: 'Clinic was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_clinic
#clinic = Clinic.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user)
.permit(:first_name, :last_name, :email, :password, :password_confirmation, :phone,
:practitioner_image,
:clinic_logo,
clinic_images: [],
profession_ids: [],
speciality_ids: [],
services_attributes: [:id, :description, :name, :duration, :price, :_destroy],
educations_attributes: [:id, :name, :place, :year, :_destroy],
membership_ids: [],
awards_attributes: [:id, :name, :year, :_destroy],
clinics_attributes: [:id, :clinic_name, :clinic_address, :clinic_zip_code, :clinic_municipality, :clinic_about, :clinic_mail, :clinic_phone, :clinic_website, :clinic_city, :_destroy,
practitioners_attributes: [:id, :public_health_insurance, :practitioner_gender, :practitioner_first_name, :practitioner_last_name, :practitioner_description, :practitioner_mail, :practitioner_phone, :practitioner_website, :_destroy]])
end
end
new.html.erb
<div id="ClinicGenerel" class="TabBlock">
<div class="content">
<div class="content clinic">
<h2 class="page-title">Generel information</h2>
<%= simple_form_for [#clinic] do |f| %>
<%= render 'clinics_fields', :f => f %>
<div class="submit-container">
<%= f.submit "Gem", :class => 'btn blue' %>
</div>
<% end %>
</div>
</div>
</div>
Log
Started POST "/clinics" for ::1 at 2020-03-09 20:35:16 +0100
Processing by ClinicsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"lkftNxR96kkoI4+m00fSevQC+dZU9KsqhvWrcWg+7RPNWd593lPj2aWBdM2vfX83k4t2WUb2LODPFJVnFwJkZg==", "clinic"=>{"clinic_name"=>"Testin", "clinic_address"=>"add", "clinic_zip_code"=>"34334", "clinic_city"=>"adsd", "clinic_municipality"=>"sadsa", "clinic_about"=>"dasds", "clinic_mail"=>"kvnana#yaoo.dk", "clinic_phone"=>"24210566", "clinic_website"=>""}, "commit"=>"Gem"}
User Load (0.8ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 96 ORDER BY `users`.`id` ASC LIMIT 1
↳ app/controllers/clinics_controller.rb:25
Completed 500 Internal Server Error in 5ms (ActiveRecord: 0.8ms)
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
app/controllers/clinics_controller.rb:25:in `create'
Change your new.html.erb to
<div id="ClinicGenerel" class="TabBlock">
<div class="content">
<div class="content clinic">
<h2 class="page-title">Generel information</h2>
<%= simple_form_for [#clinic] do |f| %>
<%= render 'clinics_fields', :f => f %>
<div class="submit-container">
<%= f.submit "Gem", :class => 'btn blue' %>
</div>
<% end %>
</div>
</div>
</div>
and try again.
<%= f.simple_fields_for(:clinics) do |p| %> needs to be removed since <%= f.simple_fields_for(:clinics) do |p| %> will try to loop over clinics association of clinic which doesn't exist.
From the codebase you shared, it seems you confused how to create nested relationship in a single request. i.e., Create user and clinic in single request per say in your case above.
There are couple of solution to just make it work.
Refer right parameters
Parameters: {"utf8"=>"✓", "authenticity_token"=>"1MpJgYCodgCLbJI2i5pZEjAV/a0qvJRHuLaSaim9Y3byHDAAqa4IbogbJNEzPTpyDNMRM3Wz5UFRU00CcBOYBQ==", "clinic"=>{"clinics"=>{"clinic_name"=>"My clinic", "clinic_address"=>"sdd", "clinic_zip_code"=>"343443", "clinic_city"=>"sadsasa", "clinic_municipality"=>"dsd", "clinic_about"=>"sasd", "clinic_mail"=>"kvnirva#yaho.dk", "clinic_phone"=>"24210866", "clinic_website"=>""}}, "commit"=>"Gem"}
If you look at your log, you won't find any key clinics_attributes but you are trying to get via params[:clinics_attributes] in create action which will return nil
Quick Fix: Replace params[:clinics_attributes] with params[:clinic][:clinics] in you create action
Better version is what suggested by #sahil-grover in above answer.
But you still have to understand the structure of params and adjust how you access it.
With that change you will need to replace params[:clinics_attributes] with params[:clinic] in you create action
Little better version to solution 2 is to use strong params(user_params in your case)
I think I got it working by changing #clinic = current_user.clinics.new(params[:clinic]) to #clinic = current_user.clinics.new(clinic_params) and instead of def user_params I've added this
def clinic_params
params.require(:clinic).permit(:id, :clinic_name, :clinic_address, :clinic_zip_code, :clinic_municipality, :clinic_about, :clinic_mail, :clinic_phone, :clinic_website, :clinic_city)
end

ActionController throws UnknownFormat while uploading a picture

Im trying to upload a photo through my Rails app and everything is fine until I click on "update...". Then it throws me an "ActionController::UnknownFormat" error and fails. Here you can find the form I'm using, the update controller and the model I'm referring to.
Form:
<%= form_with(model: current_user, local: true, html: {multipart: true}) do |form| %>
<div class="field">
<%= form.label :profile_pic %>
<%= form.file_field :profile_pic, id: :profile_pic %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
Update method:
def update
#user = current_user
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
render action: :edit
end
end
end
Error seems to be here:
respond_to do |format|
model:
require 'digest'
class User < ApplicationRecord
mount_uploader :profile_pic, ProfilePicUploader
attr_accessor :password
before_save :encrypt_new_password
after_create :build_profile
has_one :profile
has_many :trips
has_many :comments, through: :trips, source: :comments
has_many :posts, through: :trips, source: :posts
scope :recent_comments, ->{where("comments.created_at > ? AND user_id = ?", [6.months.ago, self.id]).limit(3)}
#friends
has_many :users
validates :email, uniqueness: {case_sensitive: false, message: 'El correo debe ser único'}, length: {in: 6..50, too_short: "debe tener al menos %{count} caracteres"}, format: {multiline: true,with: /^.+#.+$/, message: "formato de correo no valido"}
validates :password, confirmation: true, length: {within: 4..20}, presence: {if: :password_required?}
validates :password_confirmation, presence: true
def self.authenticate(email,password)
user = find_by_email(email)
return user if user && user.authenticated?(password)
end
def authenticated?(password)
self.hashed_password == encrypt(password)
end
protected
def encrypt_new_password
return if password.blank?
self.hashed_password = encrypt(password)
end
def password_required?
hashed_password.blank? || password.present?
end
def encrypt(string)
Digest::SHA1.hexdigest(string)
end
def build_profile
Profile.create(user: self, name: self.name, bio:"Im using Tripper!")
end
end
If anyone could tell me please what did I do wrong...
You have used the respond_to block, but you didn't specify the format in which edit action should be rendered.
Try updating the update action as below:
def update
#user = current_user
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render 'edit'} # Specify the format in which you are rendering the 'edit' page
format.json { render json: #user.errors } # If required, specify a json format as well
end
end
end
Try to the following by updating the update action
def update
#user = current_user
respond_to do |format|
if #user.update(user_params)
flash[:notice] = 'User was successfully updated.'
format.html {redirect_to user_path(#user) }
format.json { render :show, status: :ok, location: #user }
else
render action: :edit
end
end
end
Hope to help

Rails 5 Nested attributes "Unpermitted parameter" - Whitelisted

Error: Unpermitted parameter: properties
I'm whitelisting the properties{} in the request_controller.rb
This usually works but not this time.
I'm not been able to save some of the data entered in a form. The 3 fields that are not saving are coming from a dynamic form "request_type". I followed Rails Cast episode 403 for this solution, which I have working well in another project but not in this one.
Source: http://railscasts.com/episodes/403-dynamic-forms
Sorry if this is a duplicate question, but I've looked at several other questions and I can't pin-point what I'm doing wrong here
I've researched several questions here, but I'm still not able to get it to work:
Rails 4 Nested Attributes Unpermitted Parameters
Nested attributes - Unpermitted parameters Rails 4
I'm omitting some stuff to make it easier to read the code. Please ask me if you need to see more.
Here's the log:
Processing by RequestsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8EASewOIxY58b+SU+dxd2YAfpjt38IdwNSju69RPwl/OKfx3AfmvLav79igj8CqPbDwi0eJAwojRbtm+C9F6wg==", "request"=>{"name"=>"asdasddaa", "due_date(1i)"=>"2016", "due_date(2i)"=>"9", "due_date(3i)"=>"15", "user_id"=>"1", "project_id"=>"1", "request_type_id"=>"2", "properties"=>{"Name and last name"=>"asdasd", "Mobile"=>"asdada", "Office tel."=>"asdadas"}}, "commit"=>"Create Request"}
Unpermitted parameter: properties
Update
If I change the request_params to this:
def request_params
params.require(:request).permit(:name, :due_date, :group_id, :user_id, :project_id, :request_type_id, properties:{} ).tap do |whitelisted|
whitelisted[:properties] = params[:request][:properties]
end
end
See:
properties:{}
I get this Error:
Unpermitted parameters: Name and last name, Mobile, Office tel.
request_controller.rb
def new
#request = Request.new
#request_type = RequestType.find(params[:request_type_id])
#project = #request_type.project.id
end
def create
#request = Request.new(request_params)
respond_to do |format|
if #request.save
format.html { redirect_to #request, notice: 'Request was successfully created.' }
format.json { render :show, status: :created, location: #request }
else
format.html { render :new }
format.json { render json: #request.errors, status: :unprocessable_entity }
end
end
end
def request_params
params.require(:request).permit(:name, :due_date, :group_id, :user_id, :project_id, :request_type_id, :properties).tap do |whitelisted|
whitelisted[:properties] = params[:request][:properties]
end
end
request_types_controller.rb
def new
#request_type = RequestType.new
#project = Project.find(params[:project])
end
def create
#request_type = RequestType.new(request_type_params)
respond_to do |format|
if #request_type.save
format.html { redirect_to #request_type, notice: 'Request type was successfully created.' }
format.json { render :show, status: :created, location: #request_type }
else
format.html { render :new }
format.json { render json: #request_type.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #request_type.update(request_type_params)
format.html { redirect_to #request_type, notice: 'Request type was successfully updated.' }
format.json { render :show, status: :ok, location: #request_type }
else
format.html { render :edit }
format.json { render json: #request_type.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_request_type
#request_type = RequestType.find(params[:id])
end
def request_type_params
params.require(:request_type).permit(:name, :project_id, properties:{}, fields_attributes: [:name, :field_type, :required, :id, :_destroy])
# params.require(:product_type).permit(:name, :product_type_id)
end
models/request.rb
class Request < ApplicationRecord
belongs_to :group
belongs_to :user
belongs_to :project
belongs_to :request_type
serialize :properties, Hash
end
models/request_type.rb
class RequestType < ApplicationRecord
belongs_to :project
has_many :fields, class_name: "RequestField"
accepts_nested_attributes_for :fields, allow_destroy: true
has_many :requests
end
models/request_field.rb
class RequestField < ApplicationRecord
belongs_to :request_type
end
views/requests/new.html.erb
<%= form_for #request do |f| %>
<%= f.fields_for :properties, OpenStruct.new(#request.properties) do |builder| %>
<% #request_type.fields.each do |field| %>
<%= render "requests/fields/#{field.field_type}", field: field, f: builder %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit class:"btn btn-primary" %>
</div>
<% end %>
Try removing :properties from your request_params in your request_controller like this:
def request_params
params.require(:request).permit(:name, :due_date, :group_id, :user_id, :project_id, :request_type_id).tap do |whitelisted|
whitelisted[:properties] = params[:request][:properties]
end
EDIT
def request_params
params.require(:request).permit(:id, :name, :due_date, :group_id, :user_id, :project_id, :request_type_id)
params.require(:properties).permit!
end!

Rails 4: issues with strong parameters and passing data

Good day, community.
First of all, I'm a newbie in Rails. I did some thing with it in College 4 years ago, and now I decided to get back on it. Lots of things changed in version 4.
Anyway, I am experiencing issues with strong parameters. Here's what I have:
I'm using Ruby 2.1, Rails 4.1.
I am trying to create a form for a hockey match with parameters (id, team_a, team_b, arena, date, score_a, score_b). team is a table (id, name) and arena is a table (id, name).
When I pass the parameters from form to the controller, the json parameters seem to be okay. But, when it is converted into match_params it is missing some values from parameters from other table. For example, I am passing arena_id: 12, but it shows arena_id: as blank.
I've spent over 5 days on this thing. Any help appreciated.
Some of the code is bellow. Let me know if you need me to provide more information...
migration data
class CreateMatches < ActiveRecord::Migration
def change
create_table :matches do |t|
t.references :team_a, default: 1 # unknown
t.references :team_b, default: 1 # unknown
t.references :arena, default: 1 # unknown
t.datetime :date
t.integer :score_a
t.integer :score_b
t.timestamps
end
add_index :matches, :team_a_id
add_index :matches, :team_b_id
add_index :matches, :arena_id
end
end
class CreateTeams < ActiveRecord::Migration
def change
create_table :teams do |t|
t.string :name, null: false
t.timestamps
end
end
end
class CreateArena < ActiveRecord::Migration
def change
create_table :arena do |t|
t.string :name, null: false
t.timestamps
end
end
end
match.rb (model)
class Match < ActiveRecord::Base
belongs_to :team_a, :class_name => 'Team'
belongs_to :team_b, :class_name => 'Team'
belongs_to :arena
end
team.rb (model)
class Team < ActiveRecord::Base
has_many :matches
accepts_nested_attributes_for :matches
end
arena.rb (model)
class Arena < ActiveRecord::Base
has_many :matches
accepts_nested_attributes_for :matches
end
matches_controller.rb
class MatchesController < ApplicationController
before_action :set_match, only: [:show, :edit, :score, :update, :destroy]
include ActionView::Helpers::DateHelper
def index
# some code
end
def show
# some code
end
def new
#match = Match.new
#teams = Team.all.order("name ASC")
#arenas = Arena.all.order("name ASC")
end
# GET /matches/1/edit
def edit
# some code
end
def create
puts YAML::dump(match_params) # Checking passed params. Output is bellow
#match = Match.new(match_params)
respond_to do |format|
if #match.save
format.html { redirect_to #match, notice: 'Match was successfully created.' }
format.json { render action: 'show', status: :created, location: #match }
else
format.html { render action: 'new' }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
end
def update
end
def destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_match
#match = Match.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def match_params
params.require(:match).permit(:date, :score_a, :score_b, team_a_id: [:id, :name], team_b_id: [:id, :name], arena_id: [:id, :name])
end
public
end
teams_controller.rb
class TeamsController < ApplicationController
before_action :set_team, only: [:show, :edit, :update, :destroy]
layout :false
def index
#teams = Team.all
end
def show
end
def new
#team = Team.new
end
def edit
end
def create
#team = Team.new(team_params)
respond_to do |format|
if #team.save
format.json { render action: 'show', status: :created, location: #team }
format.html { redirect_to #team, notice: 'Team was successfully created.' }
else
format.html { render action: 'new' }
format.json { render json: #team.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #team.update(team_params)
format.json { head :no_content }
format.html { redirect_to #team, notice: 'Team was successfully updated.' }
else
format.html { render action: 'edit' }
format.json { render json: #team.errors, status: :unprocessable_entity }
end
end
end
def destroy
#team.destroy
respond_to do |format|
format.html { redirect_to teams_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_team
#team = Team.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def team_params
params.require(:team).permit(:name)
end
end
arenas_controller.rb
class ArenasController < ApplicationController
before_action :set_arena, only: [:show, :edit, :update, :destroy]
layout false
def index
#arena = Arena.all
end
def show
end
def new
#arena = Arena.new
end
def edit
end
def create
#arena = Arena.new(arena_params)
respond_to do |format|
if #arena.save
format.json { render action: 'show', status: :created, location: #arena }
format.html { redirect_to #arena, notice: 'Arena was successfully created.' }
else
format.html { render action: 'new' }
format.json { render json: #arena.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #arena.update(arena_params)
format.json { head :no_content }
format.html { redirect_to #arena, notice: 'Arena was successfully updated.' }
else
format.html { render action: 'edit' }
format.json { render json: #arena.errors, status: :unprocessable_entity }
end
end
end
def destroy
#arena.destroy
respond_to do |format|
format.html { redirect_to arenas_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_arena
#arena = Arena.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def arena_params
params.require(:arena).permit(:name)
end
end
matches/_match.html.erb
<%= form_for(#match, html: {role: 'form', class: 'form-horizontal'}) do |f| %>
<% if #match.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#match.errors.count, "error") %> prohibited this match from being saved:</h2>
<ul>
<% #match.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label 'Home Team' %>
<%= f.collection_select :team_a_id, #teams, :id, :name, {prompt: true}, {class: ''} %>
<%= f.label 'Visitor Team' %>
<%= f.collection_select :team_b_id, #teams, :id, :name, {prompt: true}, {class: ''} %>
<%= f.label 'Arena' %>
<%= f.collection_select :arena_id, #arenas, :id, :name, {prompt: true}, {class: ''} %>
<%= f.label 'Date' %>
<%= f.datetime_select :date, class: 'form-control' %>
<%= f.submit value: 'Submit' %>
<% end %>
And here's what I am getting in console after dumping data:
Started POST "/matches" for 127.0.0.1 at 2014-05-06 18:24:20 -0700
Processing by MatchesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0RJjnpczVkp2unG9VITyHYC89ThgELn5kVE2wYRymBU=", "match"=>{"team_a_id"=>"24", "team_b_id"=>"27", "arena_id"=>"21", "date(1i)"=>"2014", "date(2i)"=>"5", "date(3i)"=>"6", "date(4i)"=>"18", "date(5i)"=>"24"}, "commit"=>"Update"}
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 ORDER BY `users`.`id` ASC LIMIT 1
--- !ruby/hash:ActionController::Parameters
date(1i): '2014'
date(2i): '5'
date(3i): '6'
date(4i): '18'
date(5i): '24'
team_a_id:
team_b_id:
arena_id:
(0.2ms) BEGIN
SQL (1.5ms) INSERT INTO `matches` (`created_at`, `date`, `arena_id`, `team_a_id`, `team_b_id`, `updated_at`) VALUES ('2014-05-07 01:24:20', '2014-05-07 01:24:00', NULL, NULL, NULL, '2014-05-07 01:24:20')
(0.2ms) COMMIT
Redirected to http://localhost:3000/matches/90
Completed 302 Found in 13ms (ActiveRecord: 2.4ms)
Take a look at your match_params, and compare it to what parameters are being passed to your controller from your form.
def match_params
params.require(:match).permit(:date, :score_a, :score_b, team_a_id: [:id, :name], team_b_id: [:id, :name], area_id: [:id, :name])
end
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0RJjnpczVkp2unG9VITyHYC89ThgELn5kVE2wYRymBU=", "match"=>{"team_a_id"=>"24", "team_b_id"=>"27", "arena_id"=>"21", "date(1i)"=>"2014", "date(2i)"=>"5", "date(3i)"=>"6", "date(4i)"=>"18", "date(5i)"=>"24"}, "commit"=>"Update"}
You're permitting your arena_id in match_params as an array called area_id, with elements :id and :name. However, it's being passed from your form as just arena_id. You should change your match_params function to:
def match_params
params.require(:match).permit(:date, :score_a, :score_b, :team_a_id, :team_b_id, :arena_id)
end
Note that I've also changed :team_a_id and :team_b_id to be consistent with what's being passed in your parameters too, although it doesn't look like you're passing :score_a or :score_b. You should check out strong parameters in the rails guides for more information.
Okay, I found my mistake. (Thanks to JKen13579)
I have put params in the wrong place.
It should be something like that:
def match_params
params.require(:match).permit(:date, :score_a, :score_b, :team_a_id, :team_b_id , :arena_id)
end
def team_params
params.require(:team).permit(:name, matches_params:[:id, :match_id, :name])
end
def arena_params
params.require(:arena).permit(:name, matches_params:[:id, :match_id, :name])
end
It fixed the issue.
everyting but name will be removed when you call this:
params.require(:arena).permit(:name)

Devise 3 (rails 4) can't update user without password

I'm trying to update a user without having to provide a password, but approaches that worked on older devise/rails versions no longer work with devise 3 and rails 4 strong parameters.
I'm using my user_controller to update but I have also tried using a custom devise registration controller with devise_parameter_sanitizer, without success.
The form does not require a password (has no password field) and the user_controller handling the update looks like so:
# PATCH/PUT /users/1
def update
if user_params[:password].blank?
Rails.logger.info "entered if statement"
user_params.delete :password
user_params.delete :password_confirmation
Rails.logger.info(user_params.inspect)
end
#user = current_user
if #user.update(user_params)
redirect_to #user, notice: 'User was successfully updated.'
else
Rails.logger.info(#user.errors.inspect)
render action: 'edit'
end
end
private
def user_params
params.require(:user).permit(:screen_name, :full_name, :email, :about,
:location, :profile_pic, :password, :password_confirmation, :current_password)
end
.. the log after a submit looks like:
Started PATCH "/users/13" for 127.0.0.1 at 2013-05-29 11:18:18 +0100
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"20avah2OzaOVubAiam/SgvbYEQ4iijEWQqmNo7xD4rY=", "user"=>{"screen_name"=>"Darcbar", "full_name"=>"Barry Darcy", "about"=>"", "location"=>"", "website_url"=>"", "twitter_username"=>"", "email"=>"barry#gmail.com"}, "commit"=>"Save changes", "id"=>"13"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 13 ORDER BY "users"."id" ASC LIMIT 1
Entered if statement...
{"screen_name"=>"Darcbar", "full_name"=>"Barry Darcy", "email"=>"barry#gmail.com", "about"=>"", "location"=>"", "twitter_username"=>"", "website_url"=>""}
(0.2ms) BEGIN
User Exists (0.8ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'barry#gmail.com' AND "users"."id" != 13) LIMIT 1
(0.2ms) ROLLBACK
#<ActiveModel::Errors:0x007fedf45bb640 #base=#<User id: 13, username: "darcbar", full_name: "Barry Darcy", about: "", location: "", email: "barry#gmail.com", encrypted_password: "$2a$10$Mb4zsRPPqZ9CYz0zdLMBU.62NyIk/T8s6Zw/uRTwWov3...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 9, current_sign_in_at: "2013-05-28 17:51:20", last_sign_in_at: "2013-05-28 16:42:52", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", authentication_token: nil, created_at: "2013-05-27 14:03:41", updated_at: "2013-05-28 17:51:20", screen_name: "Darcbar", profile_pic_file_name: nil, profile_pic_content_type: nil, profile_pic_file_size: nil, profile_pic_updated_at: nil>,
#messages={:password=>["please enter a password with at least 5 characters", "please enter a password with at least 5 characters"]}>
Rendered users/edit.html.haml within layouts/application (3.0ms)
Rendered partials/head/_user_options.haml (1.8ms)
Completed 200 OK in 74ms (Views: 12.1ms | ActiveRecord: 1.7ms)
Does anyone know why the password errors are present?
The password validation is coming from the user model:
validates :password, presence: true
The solution is to only validate presence on create and allow_blank on update:
validates :password, presence: true, length: {minimum: 5, maximum: 120}, on: :create
validates :password, length: {minimum: 5, maximum: 120}, on: :update, allow_blank: true
As of 2014, you can simply override a protected method and do:
class RegistrationsController < Devise::RegistrationsController
protected
def update_resource(resource, params)
resource.update_without_password(params)
end
end
You can use #user.update_without_password(user_params) method to update your other fields.
For example, I have this in my custom users_controller.rb. I update with remote call (ajax).
#users_controller.rb
def update
respond_to do |format|
if needs_password?(#user, user_params)
if #user.update_with_password(user_params_password_update)
flash[:success] = 'User was successfully updated. Password was successfully updated'
format.js {render 'update'}
else
error = true
end
else
if #user.update_without_password(user_params)
flash[:success] = 'User was successfully updated.'
format.js {render 'update'}
else
error = true
end
end
if error
flash[:error] = #user.errors.full_messages.join(', ')
format.js {render json: #user.errors.full_messages, status: :unprocessable_entity}
end
end
end
private
def needs_password?(user, user_params)
!user_params[:password].blank?
end
def user_params
params[:user].permit(:email, :password, :password_confirmation, :username, :full_name)
end
#Need :current_password for password update
def user_params_password_update
params[:user].permit(:email, :password, :password_confirmation, :current_password, :username, :full_name)
end
The key is in this "user_params[:password].blank?". The next is a example of the code:
def update
if user_params[:password].blank?
params = user_params_without_password
else
params = user_params
end
respond_to do |format|
if #user.update(params)
format.html { redirect_to #user, notice: t(:user_update) }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:email, :username, :first_name, :last_name, :admin, :locked, :password)
end
def user_params_without_password
params.require(:user).permit(:email, :username, :first_name, :last_name, :admin, :locked)
end
Hope you help
I went round in circles on this for ages. The answers are all in validatable as suggested by mrstif above. If you use the validatable module Devise works out of the box (with configuration options) allowing you to update user details without supplying a password so be very careful about rolling your own password validations.
simply override the Devise by creating app/controller/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
protected
def update_resource(resource, params)
resource.update(params.except(:current_password))
end
end
this code will directly update user params except :current_password
and update config/routes.rb
devise_for :users, controllers: {registrations: 'registrations'}
My goal was to enable editing user attributes without requiring a password, unless it's changing email, password or deleting the account. And here's what worked for me:
app/controllers/registrations_controller.rb:
class RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters
...
def update
params[:user][:team_attributes][:id] = current_user.team.id
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
if password_required?
successfully_updated = resource.update_with_password(account_update_params)
else
account_update_params.delete(:current_password)
successfully_updated = resource.update_without_password(account_update_params)
end
if successfully_updated
sign_in resource, bypass: true
redirect_to '/'
else
render :edit
end
end
def destroy
current_password = devise_parameter_sanitizer.sanitize(:account_update)[:current_password]
resource.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
error_messages = 'Current password ' + resource.errors[:current_password].join
if resource.destroy_with_password(current_password)
redirect_to '/'
else
redirect_to delete_account_path, notice: error_messages
end
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:account_update) do |user_params|
user_params.permit(:username, :email, :password, :password_confirmation, :current_password, :name, :phone_number
end
end
private
def password_required?
(resource.email != params[:user][:email] if params[:user][:email].present?) || params[:user][:password].present?
end
end
Update config/routes.rb:
devise_for :users, controllers: { registrations: 'registrations' }
In views/devise/registrations/edit.html.haml
# edit form
...
= simple_nested_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: 'mo-form' }, defaults: { placeholder: false, hint: false }) do |f|
...
# delete form
...
= simple_form_for(resource, as: resource_name, url: user_registration_path(resource_name), method: :delete, html: { class: 'mo-form' }, defaults: { placeholder: false, hint: false }) do |f|
...
I made use in that case of the method update_columns.
It avoids having to bypass password validation for instance.
#user.update_columns(user_params)
However, this method will not modify the timestamp of the updated_at field and this also needs to be addressed, either by merging { updated_at: Time.current } or by a touch.

Resources