Adding Custom Fields to Devise

By:, On:

This tutorial assumes you have already completed:

  1. Simple HTML Refactoring

Once you setup Devise, you may soon realize that you need to allow users to save more information about themselves. What about name, gender or date of birth? Sometimes we need a little more than just an email.

Step 1: Run Rails Generate Device

If you carefully explore app/views in our application you will notice that there is no HTML templates for all the login, registration or password reminding forms. This is because all the forms are sitting inside Devise gem. If we want to edit the forms we need to ask Devise to export all templates to our views folder.

Inside the folder with your application, run this in your terminal:

bash


$ rails generate devise:views users

After we run this command, you will find a bunch of new folders under app/views/users. These are the forms we will be using to register users, log them in, and reset passwords. We can try to edit one of the templates and see what happens.

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

<h2>Sign up</h2>
<%= 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>

  <div><%= f.label :password %>

      <%= f.password_field :password, autocomplete: "off" %></div>

  <div><%= f.label :password_confirmation %>

      <%= f.password_field :password_confirmation, autocomplete: "off" %></div>

  <div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "users/shared/links" %>

Step 2: Change and Save Headline

Let's change the header of the page "Sign up" to "Register"

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


<h2>Register</h2>

Now, if you logout and try to register, you would expect to see the new title of the page "Register." However, you will still see "Sign up." This is because we need to adjust the configuration of Devise. Go to config/initializers/devise.rb and look for the line:

config/initializers/devise.rb


  # config.scoped_views = false

uncomment the line and change to "true":


  config.scoped_views = true

Restart your application using rails server in your terminal and go to registration page again. You will see the "Register" title this time.

Step 3: Run "Migration"

Great, we can now focus on adding more information to the user form. Before we modify the form we need to make sure our model can take it. At the moment our users can have an email and password, both were generated by Devise. We need to add "name" to the database table for users. We will do this with migration - a special file to update structure of the database. In bash:

bash


$: rails generate migration add_name_to_users name:string

    invoke  active_record
    create    db/migrate/20140519054104_add_name_to_users.rb

Step 4: Check and Modify Migration File

Let's make sure the generated file is correct. Make sure it looks like this:

db/migrate/20140519054104_add_name_to_users.rb


class AddNameToUsers < ActiveRecord::Migration
  def change
    add_column :users, :name, :string
  end
end

At this point you can freely edit this migration file. For example, you can still add some other fields like gender or date of birth. We will include them here in case you'd like to do it as well. We will try to use different data formats:


class AddNameToUsers < ActiveRecord::Migration
  def change
    add_column :users, :name, :string
    add_column :users, :date_of_birth, :datetime
    add_column :users, :is_female, :boolean, default: false
  end
end

Notice how we used boolean (true or false) value for gender instead of using string. Of course, if you want to allow more genders (for whatever reason) feel free to implement it in a different way.

Step 5: Migrate Database

We can now migrate our database:

bash


$ rake db:migrate

    == 20140519054104 AddNameToUsers: migrating =======================
    -- add_column(:users, :name, :string)
    -> 0.0016s
    -- add_column(:users, :date_of_birth, :datetime)
    -> 0.0005s
    -- add_column(:users, :is_female, :boolean, {:default=>false})
    -> 0.0029s
    == 20140519054104 AddNameToUsers: migrated (0.0052s) ==============

Step 6: Edit Users

We can now try editing our existing user. Let's start by adding a link to the page where we can edit user's profile:

app/views/layouts/_menu.html.erb


<nav class="navbar navbar-default" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <%= link_to "Demo App", root_path, class: 'navbar-brand' %>
        </div>

        <div class="collapse navbar-collapse" id="navbar-collapse-1">
            <ul class="nav navbar-nav navbar-right">
                <li><%= link_to 'Posts', '#' %></li>
                <% if current_user %>
                    <li><%= link_to 'Edit Profile',edit_user_registration_path %></li>
                    <li><%= link_to 'Logout', destroy_user_session_path, method: :delete %></li>
                <% else %>
                    <li><%= link_to 'Login', new_user_session_path %></li>
                <% end %>
            </ul>
        </div>
    </div>
</nav>

And add our new fields to this page:

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


    <h2>Edit <%= resource_name.to_s.humanize %></h2>

    <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
    <%= devise_error_messages! %>

    <div><%= f.label :name %><br />
        <%= f.text_field :name, autofocus: true %></div>

    <div><%= f.check_box :is_female, {}, true %> <%= f.label :is_female, "Female" %> </div>
    <div><%= f.check_box :is_female, {}, false %> <%= f.label :is_female, "Male" %> </div>

    <div><%= f.label :date_of_birth %><br />
        <%= f.date_select :date_of_birth %></div>

    <div><%= f.label :email %><br />
        <%= f.email_field :email %></div>

    <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
    <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
    <% end %>

    <div><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
        <%= f.password_field :password, autocomplete: "off" %></div>

    <div><%= f.label :password_confirmation %><br />
        <%= f.password_field :password_confirmation, autocomplete: "off" %></div>

    <div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
        <%= f.password_field :current_password, autocomplete: "off" %></div>

    <div><%= f.submit "Update" %></div>
    <% end %>

    <h3>Cancel my account</h3>

    <p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>

    <%= link_to "Back", :back %>


Step 7: Save information

Now, we can try to edit our profile with some more precise information and attempt to save.

editing custom fields in user profile

After you save the information and go back to the page, you will find that none of the custom fields has been saved:

failed to save custom information

This is because the way Rails 4 deals with "mass assignment." Due to security reasons we have to explicitly permit parameters inside each controller. That also concerns the controllers build in the Devise. We won't be customizing them now, instead we will follow guidelines provided by Devise team and add permissions to application controller:

app/controllers/application_controller.rb


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 :configure_permitted_parameters, if: :devise_controller?

    protected

        def configure_permitted_parameters
            devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password) }
            devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:name, :email, :password, :current_password, :is_female, :date_of_birth) }
        end
end

Every time you want to edit permissions for our users, you would have to do it here. Notice how we're specifying permissions separately for sign ups and updating account. After you save those changes you can try editing your profile again. This time it will work.

If you login you will be able to mark this tutorial as finished to track your progress



Comments

  • On: D wrote:

    The gender returns a true or false instead of male or female on my show page. How do I get the selections to show? Thanks
  • On: Brunitob wrote:

    Im having some problem with heroku, I get: We're sorry, but something went wrong.

  • On: Brunitob wrote:

    I forgot to run: heroku run rake db:migrate

  • On: Suresh Kumar R wrote:

    Hi

    Could you please the gender (male and female) as radio buttons please instead of check boxes?

  • On: Suresh Kumar R wrote:

    Hi Lucasz,

    Can I use radio buttons as follows?

              <%= f.radio_button :is_female, '1' %> <%= f.label :is_female, "Female" %>
              <%= f.radio_button :is_female, '0' %> <%= f.label :is_female, "Male" %
    
  • On: zhu_jinlong wrote:

    of course you can

  • On: Hendrix wrote:

    Hola segui los pasos que indicaste y no me funciona el login ahora.

  • On: Fabiola Cabrera wrote:

    Hola Hendrix, disculpa que te respondamos tan tarde. Cual es el error que te aparece?

  • On: Hendrix wrote:

    Hola Fabiola Cabrera, ya resolvi. Gracias.

  • On: Giancarlo wrote:

    Pero mi css se distorsiona!

  • On: Fabiola Cabrera wrote:

    Como que se te distorsiona? Que es lo que te aparece?

  • On: Pablo wrote:

    Hola que tal, seguí los pasos que indicas para agregar un nuevo campo pero cuando lo imprimo desde el controlador SessionsController me sale vacío, si imprime el correo y la clave encriptada pero el campo que agregue no. Por favor Ayuda...

  • On: carolina wrote:

    Hola!! gracias por los tutoriales están muy buenos!!!, pero no me esta generando los campos que agregue al formulario no realiza ningún cambio como el titulo, cambie el nombre pero sigue igual que puede ser? estoy usando ruby 2.0.0 y rails 4.2.4

  • On: Armando wrote:

    Excelente tutorial, claro y preciso. Gracias!!!

  • On: Tunde Adetula wrote:

    i have validated the custom fields i want in my users but now when i try to signup, it keeps asking me for that validation i.e DOB no present is there a way around that that please. A way to exempt DOB when user wants to sign up

  • On: Roy wrote:

    Buenisimo!!! gracias xD!!!

  • On: Carles Bas wrote:

    Para Rails 4 han cambiado algunos parámetros para application_controller

    The Parameter Sanitaizer API has changed for Devise 4 class ApplicationController < ActionController::Base beforeaction :configurepermittedparameters, if: :devisecontroller?

    protected

    def configurepermittedparameters

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

    end end

    via: stackoverflow

Comment

You can login to comment