Rails 4.0 with Devise. Nested attributes Unpermited parameters
Solution 1
config/routes.rb
Create your own registration controller like so ... (see Devise documentation for the details of overriding controllers here ...) ... which is more elegant way as opposed to doing it via the ApplicationController
devise_for :users, controllers: {registrations: 'users/registrations'}
app/controllers/users/registrations_controller.rb
Override the new method to create a Profile
associated with the User
model as below ... run the configure_permitted_parameters
method before to sanitize the parameters (note how to add nested parameters)
class Users::RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters
# GET /users/sign_up
def new
# Override Devise default behaviour and create a profile as well
build_resource({})
resource.build_profile
respond_with self.resource
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u|
u.permit(:email, :password, :password_confirmation, :profile_attributes => :fullname)
}
end
end
db/migrate/xxxxxxxxxxxxxx_create_profiles.rb
This is the migration that generates the Profile
model (note the reference to User
) ... this example profile only keeps fullname
as an extension of the User
but feel free to add as you wish!
class CreateProfiles < ActiveRecord::Migration
def change
create_table :profiles do |t|
t.references :user
t.string :fullname
t.timestamps
end
end
end
app/models/user.rb
class User < ActiveRecord::Base
# Associations
has_one :profile, dependent: :destroy, autosave: true
# Allow saving of attributes on associated records through the parent,
# :autosave option is automatically enabled on every association
accepts_nested_attributes_for :profile
# Devise
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
app/models/profile.rb
class Profile < ActiveRecord::Base
# Associations
belongs_to :user
# Validations
validates :fullname, presence: true
end
app/views/devise/registrations/new.html
<% resource.build_profile if resource.profile.nil? %>
<%= form_for(resource, :as => resource_name,
:url => registration_path(resource_name)) do |f| %>
<ul>
<%= devise_error_messages! %>
<li class="fullname">
<%= f.fields_for :profile do |profile_fields| %>
<%= profile_fields.label :fullname %>
<%= profile_fields.text_field :fullname %>
<% end %>
</li>
<li class="email">
<%= f.label :email %>
<%= f.email_field :email, :autofocus => true %>
</li>
<li class="password">
<%= f.label :password %>
<%= f.password_field :password %>
</li>
<li class="password">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</li>
<li>
<%= f.submit %>
</li>
<li>
<p><%= render "devise/shared/links" %></p>
</li>
</ul>
<% end %>
Solution 2
You must create your own registration controller to do so, here is how:
routes.rb
devise_for :users, controllers: {registrations: 'registrations'}
Controller
You must replace :your_fields
by the fields you want to allow (sorry if I leave that to you, but that makes my answer more general, therefore usable for anyone that would pass by)
class RegistrationsController < Devise::RegistrationsController
private
def sign_up_params
allow = [:email, :your_fields, :password, :password_confirmation]
params.require(resource_name).permit(allow)
end
end
Additional info (nested attributes + some testing)
Also note that if you are using association and accepts_nested_attributes_for
you will have params
structured like this
model: {field, field, field, associated_model: {field, field}}
And off course you must use the same structure in your sign_up_params
method. If you need to understand this, you can change the content of sign_up_params
method like this:
def sign_up_params
params.require(resource_name).permit!
end
That will allow any param, then post your form (it should pass this time) and look into your rails console to see the structure of params
, finally you can set-up sign_up_params
method correctly
Check this for more info http://www.railsexperiments.com/using-strong-parameters-with-nested-forms/
In your case you should use:
params.require(resource_name).permit( :email, :first_name, :last_name, institutions: [:name], :password, :password_confirmation )
Solution 3
Using rails 5.1 and devise 4.4.1 following is the shortest and works pretty good:
app/models/user.rb
after_initialize do
build_profile if new_record? && profile.blank?
end
app/controllers/application_controller.rb
before_action :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [{ profile_attributes: :name }])
end
The key here is that you can do following without making separate controller:
- permit nested attributes
- build relation for form builder
Related videos on Youtube
Claudiu S
Updated on June 04, 2022Comments
-
Claudiu S almost 2 years
I am working on a web-app using Devise and Rails 4. I have a User model which I have extended with 2 extra form fields such that when a user signs up he can also submit his first/last names. (based on http://blog.12spokes.com/web-design-development/adding-custom-fields-to-your-devise-user-model-in-rails-4/). I now want to add a Institution model. This model has_many :users, and a user belongs_to :institution. I want to be able to register the institution's name on the same form I register the user. I know I need a nested_attribute in my Institution model, since this is the parent, which I will show in a bit. When I try to sign up the user I get in the console: Unpermited parameters: Institutions.
My hint is that I cannot update my parent class(Institution) based upon my child class (User). Might there be a solution to this? Or has anyone experienced something similar?
class Institutions < ActiveRecord::Base has_many :users, accepts_nested_attributes_for :users end class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable belongs_to :institution end
registrations/new.html.erb Here I have the nested form
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> <%= devise_error_messages! %> . . <%= f.fields_for :institutions do |i| %> <p><%= i.label :name %><br /> <%= i.text_field :institutions_attr %></p> <% end %>
Based on the tutorial I have linked earlier, I have created a new User::ParameterSanitizer which inherits from the Devise::ParameterSanitizer and overridden the
sign_up
method as follows:lib/user_sanitizer.rb
private def sign_up default_params.permit(:first_name, :last_name ,:email, :password, :password_confirmation, :current_password, institutions_attributes: [:id, :name]) end
Finally, my application_controller.rb
class ApplicationController < ActionController::Base protect_from_forgery with: :exception protected def devise_parameter_sanitizer if resource_class == User User::ParameterSanitizer.new(User, :user, params) else super end end end
Thank you for reading!
Console params output:
{"utf8"=>"✓", "authenticity_token"=>"JKuN6K5l0iwFsj/25B7GKDj7WEHR4DO3oaVyGxGJKvU=", "user"=>{"email"=>"[email protected]", "first_name"=>"abc", "last_name"=>"xyz", "institutions"=>{"name"=>"Government"}, "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
EDIT
As suggested, I have added
params.require(resource_name).permit( :email, :first_name, :last_name, institution: [:name], :password, :password_confirmation ) and I get an *error syntax error, unexpected ',', expecting => ...nstitution: [:name], :password, :password_confirmation )*
BUT, if I re-edit to
params.require(resource_name).permit( :email, :first_name, :last_name, :password, :password_confirmation, institution: [:name] )
I get NO syntax error but I get Unpermited parameters: Institutions in the Request.
My belief is that this happens because User is a child of Institution. I have, however, been unable to find a work-around this.
-
Claudiu S almost 11 yearsThx for the reply. I did try this. I does not work since it still does not permit the parameters of the Institution model.
-
Claudiu S almost 11 yearsI saw that ^_^. I did write it the following way: allow = [:email, :first_name, :last_name, :password, :password_confirmation, :name], where :name is a field in the other model.
-
Claudiu S almost 11 yearsI guess it has something to do with the fact that User is a child of Institution. Or maybe I am wrong
-
Benj almost 11 yearsBy the way, the part
params.require(resource_name)
rely on the fact that User is the 'parent' model, but in your case you should replaceresource_name
by:institution
. On the other hand I don't know how devise will react to that, but that's another question (we are talking here about strong params, right?) -
Claudiu S almost 11 yearsYes we are talking about strong params, and yes the User is the child in this instance. I have replaced resource_name and got this "param not found: institution", so I'm guessing it's not working.
-
Benj almost 11 years"param not found: institution" means params does not contain :institution, or it is empty. I assume you did not follow my advise on testing with
.permit!
. You must find this part of your answer by yourself now (I can't test it for you) -
Claudiu S almost 11 yearsI did exactly this: params.require(:institution).permit! and the error comes from ActionController::ParameterMissing in RegistrationsController#create. And in the request I get this "institutions"=>{"name"=>"xyz"}
-
Benj almost 11 yearsPaste your whole params line found in the console please (edit your question, it's more readeable)
-
Benj almost 11 yearsOk edited my answer, I hope that's it cause I gotta go now :) good luck
-
Benj almost 11 yearsNo problem. You can give me rep by accepting the answer, this is how stackoverflow works :) cheers
-
Benj over 9 yearsI'm still using the same technique in all my projects while keep them to the last rails version. It works I confirm
-
David Routen over 8 yearsThanks much King'ori, this is exactly what I was looking for! Saved me a few extra hours of frustration.
-
Anjan over 8 years@king'ori Maina I am using devise_auth_token gem and after falling above methods for the same gem I am still getting Unpermitted parameter: account error in my server log
-
King'ori Maina over 8 years@Anjan Unfortunately I haven't used
devise_auth_token
before so don't know where to start. I'd guess that theUnpermitted parameter: account
error means that the:account
parameter needs to be added to the allowed parameters. Seeconfigure_permitted_parameters
method in theUsers::RegistrationsController
class above on how I added profile attributes. -
Sagar Ranglani over 8 yearsThis answer should be accepted as the correct answer!