Adding extra registration fields with Devise
Asked Answered
M

8

21

I am trying to add some extra fields to registrations#new. Since I only want extra data and do not need different functionality, I don't see why I need to override controllers etc. So what I did was modify registrations#new as follows:

%h2
  Sign up
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do ||f
  = devise_error_messages!
  %div
    = f.label :email
    %br
    = f.email_field :email, autofocus: true
  %div
    = f.label :title_id
    %br
    = f.text_field :title_id
  %div
    = f.label :province_id
    %br
    = f.text_field :province_id
  %div
    = f.label :first_name
    %br
    = f.text_field :first_name
  %div
    = f.label :last_name
    %br
    = f.text_field :last_name
  %div
    = f.label :password
    %br
    = f.password_field :password
  %div
    = f.label :password_confirmation
    %br
    = f.password_field :password_confirmation
  %div= f.submit 'Sign up'
= render 'devise/shared/links'

To enable these extra fields through the sanitizer, I updated ApplicationController as follows:

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception
  before_filter :store_requested_url!
  # before_filter :authenticate_user!

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:email) }
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:email, :password, :password_confirmation, :title_id, :province_id, :first_name, :last_name) }
    devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:email, :password, :password_confirmation, :current_password) }
  end

  def after_sign_in_path_for(resource)
    session[:requested_url] || root_path
  end

  private

  def store_requested_url
    # store last url as long as it isn't a /users path
    session[:previous_url] = request.fullpath unless request.fullpath == /\/users/
  end
end

For some reason, it is not working and the extra fields go to the database as nulls.

I am using Ruby 2 and Rails 4 rc1, with Devise 3.0.0.rc.

Motoneuron answered 9/5, 2013 at 21:32 Comment(2)
Code should be posted as text, not images.Mcginn
You need a new function in the ApplicationController. github.com/plataformatec/devise#strong-parametersOrgano
M
10

OK, so what I did was just override the Devise registration controller, update routes.rb as per the devise docs to reflect this, copied and pasted the Devise code for registrations#create as is, and change the getting params part to use my own strong parameters method, and that was that.

class RegistrationsController < Devise::RegistrationsController

  def create
    build_resource(registration_params)

    if resource.save
      if resource.active_for_authentication?
        set_flash_message :notice, :signed_up if is_navigational_format?
        sign_up(resource_name, resource)
        respond_with resource, :location => after_sign_up_path_for(resource)
      else
        set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
        respond_with resource, :location => after_sign_up_path_for(resource)
      end
    else
      clean_up_passwords
      respond_with resource
    end
  end  

  private

  def registration_params
    params.require(:user).permit(:email, :title_id, :first_name, :last_name, 
      :province_id, :password, :password_confirmation)
  end

end
Motoneuron answered 12/5, 2013 at 0:11 Comment(4)
@mindtonic I just edited this post so it was a code instead of an image :)Reitz
And don't forget the : :controllers => { :registrations => 'registrations' }Chessa
@Motoneuron appreciate if u can edit your answer to point to the latest update as per other answers belowOnanism
This may be the accepted answer before, but this is not the easiest way to do it as of May 2019. Check out https://mcmap.net/q/409122/-adding-extra-registration-fields-with-devise. This worked for me.Champerty
F
32

It would appear that the code sample in your question is not working because you are not setting the before_filter to call the sanitizer.

before_filter :configure_permitted_parameters, if: :devise_controller?

With that said, it's probably better to override the controller, as shown in the accepted answer, so that the application controller isn't doing this check all of the time. The accepted answer can be shortened up with the code below. I've tested this code with my application and it works well. All of this is documented in the Strong Parameters section of the README in the 3.0.0.rc tag.

Override the controller:

class RegistrationsController < Devise::RegistrationsController
  before_filter :configure_permitted_parameters, :only => [:create]

  protected

    def configure_permitted_parameters
      devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password) }
    end
end

Then update the routes to use it:

devise_for :members, :controllers => { :registrations => "registrations" }
Flatt answered 6/6, 2013 at 23:35 Comment(1)
UPDATE FOR DEVISE 4.2: use devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:username, :email, :password) }Earhart
J
17

As of Devise version 4.3.0, May 15th 2017, the solution is as follows from the documentation. In this case, the username field is being added.

In case you want to permit additional parameters (the lazy way™), you can do so using a simple before filter in your ApplicationController:

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
  end
end

And of course, simply add the field to your database

> rails g migration AddUsernameToUsers

class AddUsernameToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :username, :string, null: false, index: true, unique: true
  end
end

And then add the necessary fields into the view for registrations#new

<%= f.text_field :username, placeholder: "Username"  %>
Joshua answered 17/5, 2017 at 21:45 Comment(0)
G
13

After Devise 4.0 the older answers on this topic are not valid. instead of the for method you have to use:

devise_parameter_sanitizer.permit(:sign_up, keys: [:username])

So, for a complete solution in ApplicationController:

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected
    def configure_permitted_parameters
       devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
    end
end
Glasgo answered 18/8, 2016 at 21:44 Comment(1)
Will this automatically add the username to Users table?Geodetic
M
10

OK, so what I did was just override the Devise registration controller, update routes.rb as per the devise docs to reflect this, copied and pasted the Devise code for registrations#create as is, and change the getting params part to use my own strong parameters method, and that was that.

class RegistrationsController < Devise::RegistrationsController

  def create
    build_resource(registration_params)

    if resource.save
      if resource.active_for_authentication?
        set_flash_message :notice, :signed_up if is_navigational_format?
        sign_up(resource_name, resource)
        respond_with resource, :location => after_sign_up_path_for(resource)
      else
        set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
        respond_with resource, :location => after_sign_up_path_for(resource)
      end
    else
      clean_up_passwords
      respond_with resource
    end
  end  

  private

  def registration_params
    params.require(:user).permit(:email, :title_id, :first_name, :last_name, 
      :province_id, :password, :password_confirmation)
  end

end
Motoneuron answered 12/5, 2013 at 0:11 Comment(4)
@mindtonic I just edited this post so it was a code instead of an image :)Reitz
And don't forget the : :controllers => { :registrations => 'registrations' }Chessa
@Motoneuron appreciate if u can edit your answer to point to the latest update as per other answers belowOnanism
This may be the accepted answer before, but this is not the easiest way to do it as of May 2019. Check out https://mcmap.net/q/409122/-adding-extra-registration-fields-with-devise. This worked for me.Champerty
H
10

First expose the views

rails generate devise:views users

then edit config/initializers/devise.rb and change

# config.scoped_views = false

to

config.scoped_views = true

this will allow you to modify the views at app/views/users/registration.

you will add the fields needed here, in both

app/views/users/registration/edit.html.erb

app/views/users/registration/new.html.erb

Now we have to deal with rails mass assignment issue, go to application_controller.rb and add a before_filter

before_filter :configure_permitted_parameters, if: :devise_controller?

then add your fields + original fields to devise sanitization

protected

    def configure_permitted_parameters
        # Fields for sign up
        devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password) }
        # Fields for editing an existing account
        devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :email, :password, :current_password, :gender) }
    end

restart your web server and cross your fingers.

Holsworth answered 7/9, 2015 at 8:52 Comment(0)
R
6

I've had similar situation (just fields were different).

Here's the way official documentation can offer: Just add this to your ApplicationController. And change "username" to whatever you need, and add some more if you need.

before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) << :username
  end

My Applications Controller looks like that:

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception

  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

    def configure_permitted_parameters
      devise_parameter_sanitizer.for(:sign_up) << :public_name
    end
end

More details here: https://github.com/plataformatec/devise ("Strong Parameters")

Roselinerosella answered 26/1, 2015 at 14:35 Comment(1)
This is the best way to do it; copy-pasting code is asking for trouble.Vaporetto
S
4

First: Isn't there a new 'strong parameters' issue with rails 4, you might want to look into this as well.

If you migrate the new parameters into your User model. Then all you have to do is to override (create) the files:

app/views/devise/registrations/edit.html.erb
app/views/devise/registrations/new.html.erb

you can look at the default files here: https://github.com/plataformatec/devise/tree/master/app/views/devise/registrations

IF you might want to implement an own registrations_controller.rb (with actions new and edit) and your own @variables then it is important to add this in your routes.rb

devise_for :users, :controllers => { :registrations => 'registrations' }
resources :users

This ensures, that devise takes your new 'registrations' controller from now on (if you decided to have one).

I don't know "sanitizer" or what this is good for. But my App works just fine with those minor changes I just recommended to you. You don't need to override the Controller! Overriding the Views will just be enough.

Symposiac answered 9/5, 2013 at 21:49 Comment(2)
I copied the views into my project. I have not overridden the controllers because I dont need to change any behaviour. The santizer seems to be a wrapper for strong params. Does your app have extra fields for registration#new?Motoneuron
Yes, hence people can pay with credit card in this app, they get a "role" depending on the plan they decide for. I have 2 additional fields in my form. You are right, you don't need to override the controller unless you want a changed behaviour.Symposiac
M
2

New fields could be added like this example. For Devise 4, the Parameter Sanitaizer API has changed:

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:username, :name])
  end
end
Margetts answered 20/7, 2020 at 14:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.