Search code examples
ruby-on-railsauthenticationdeviseroles

Adding role selection to devise sign up form, when role name needs to match role_id (using devise)


I'm new to rails (using rails 6), and I have a question that I didn't see answered on the site for my specific use case.

The basic scenario is the same as in many question - adding role based authentication to a rails application when using devise.

The way I implemented this was to create a roles table, and having a one to many relations between it and the users table.

I need only two users: guest and admin, and they have 1, 2 ids, respectively. I inserted the values into the roles table manually, using the rails console.

Now, I'm having trouble with the sign up form, which is the default one devise gem created:

<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <div class="field">
    <%= f.label :password %>
    <% if @minimum_password_length %>
    <em>(<%= @minimum_password_length %> characters minimum)</em>
    <% end %><br />
    <%= f.password_field :password, autocomplete: "new-password" %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
  </div>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

What I want to add is a select field that will show "admin" and "guest", and when the user submit the form, the right role_id will be added to the user record in the user table, but I don't get how to inform the devise view on the role table existence and how to match the string value of the role with the role id the I want the user to accualy have.

So my questions are:

  1. How to add the roles to a select field in the devise sign up form?

  2. How to handle after the user selects one of the roles string names, and match it with role id that will be added to the user when the form is processed?

I saw the following questions and many like them, but I didn't saw any of them dealing with matching string with id before the form is submitted:

Devise Add Admin Role

how to automatically add a role to a user when signing up using devise

Adding Fields To Devise Sign Up Using Rails 4

Multiple models associated with devise user - fill in one model based on selection of role

If there is a question answering my issue, I'm sorry and I would be glad to get the link and close this question.

If there is any more info you need, please let me know.

Thanks!


Solution

  • Using an enum to differentiate admins from guests seems like the best option and will prevent you from having to complicate your model/form with unnecessary things like nested attributes.

    Here's an example...

    Create a role column on the users table.

    $ rails g migration add_role_to_users role:integer

    In your migration file, make sure to set the default to 0.

    add_column :users, :role, :integer, default: 0
    

    Then, after migrating the db, in your User model add the enum...

    class User < ApplicationRecord
      enum role: { guest: 0, admin: 1 } 
      ...
    end
    

    Adding another role is as simple as adding a key/value to the role enum.

    Then, in your devise form, you can add something like...

    <%= f.label :role %>
    <%= f.select :role, User.roles.keys %>
    

    You will also need to make sure that you are adding role as a permitted param...you seem to be adding it as a field in signup, so in ApplicationController...

    class ApplicationController < ActionController::Base
      before_action :configure_permitted_parameters, if: :devise_controller?
    
      private
    
      def configure_permitted_parameters
        devise_parameter_sanitizer.permit(:sign_up, keys: [:role])
      end
    end
    

    If you go with this you will also have to remove your roles table and associations.