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
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 )
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 %>
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: