Search code examples
ruby-on-railsparametersdevisesanitization

how to write parameter sanitiser method to sanitize the parameters of other model in devise sign_up action?


i am having two tables employee , and company. i want to register the company name of an employee while Employee is registering by using the devise sign_up action. how to write devise parameter sanitiser method to save the company name while an employee is registering?


Solution

  • The trick is using accepts_nested_attributes_for and overriding the sign_up_params method on the registrations controller.

    1. Set up the User model to accept attributes for company

    class User < ActiveRecord::Base
    
      belongs_to :company
      accepts_nested_attributes_for :company
    
      # Include default devise modules. Others available are:
      # :confirmable, :lockable, :timeoutable and :omniauthable
      devise :database_authenticatable, :registerable,
             :recoverable, :rememberable, :trackable, :validatable
    end
    

    2. Override the default Registrations controller

    # config/routes.rb
    Rails.application.routes.draw do
      # ...
      devise_for :users, controllers: { registrations: "registrations" }
    end
    

    # app/controllers/registrations_controller.rb
    class RegistrationsController < Devise::RegistrationsController
    
      # GET /users/sign_up
      def new
        @user = User.new(company: Company.new)
      end
    
      def sign_up_params
        params.require(:user).permit(
          :email, :password, :password_confirmation,
          company_attributes: [:name]
        )
      end
    end
    

    By digging into the source of Devise::RegistrationsController we can se that it calls build_resource(sign_up_params) which is about equivalent to User.new(sign_up_params). So we can simply add our own params handling by declaring our own sign_up_params method.

    Note that in sign_up_paramswe use the built Rails 4 strong parameter handling instead of the Devise sanitized params which is a home rolled solution that predates Rails 4. It might be possible to do it with Devise sanitized params but there is no real reason unless you have to have backwards compatibility with Rails 3.

    3. Customize the Registration form

    To get the correct params hash we want the company name input to have the following name attribute:

    user[company_attributes][name]
    

    Rails has a nice helper called fields_for which lets us do that:

    <%# app/views/devise/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 class="field">
        <%= f.label :email %><br />
        <%= f.email_field :email, autofocus: true %>
      </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: "off" %>
      </div>
    
      <div class="field">
        <%= f.label :password_confirmation %><br />
        <%= f.password_field :password_confirmation, autocomplete: "off" %>
      </div>
    
      <fieldset>
        <legend>Company</legend>
    
        <%= f.fields_for :company do |c| %>
          <div class="field">
            <%= c.label :name %><br />
            <%= c.text_field :name%>
          </div>
        <% end %>
      </fieldset>
    
      <div class="actions">
        <%= f.submit "Sign up" %>
      </div>
    <% end %>
    
    <%= render "devise/shared/links" %>
    

    Notice that fields_for gives us a new form builder instance in the block (c) which we create our nested inputs from.

    4. Beer.