35

I want to extend the sign up form of my devise installation. I created a Profile model and am asking myself now, how can I add specific data of the form to this model. Where is the UserController of devise located?

Thanks in advance!

Josien
  • 13,079
  • 5
  • 36
  • 53
trnc
  • 20,581
  • 21
  • 60
  • 98
  • 8
    Why create another model profile? Why not just have everything in user. – AnApprentice Jun 14 '11 at 19:13
  • 2
    @AnApprentice separation of concerns. You'd want to have an Account or User model for everything related to user account and Profile model for personal profile data. You may also need to allow users to have a very comprehensive profile and you don't want to create a 50+ fields table. – Omar Ali Aug 03 '16 at 13:57

5 Answers5

45

Assuming you have a User model with a has_one Profile association, you simply need to allow nested attributes in User and modify your devise registration view.

Run the rails generate devise:views command, then modify the devise registrations#new.html.erb view as shown below using the fields_for form helper to have your sign up form update your Profile model along with your User model.

<div class="register">
  <h1>Sign up</h1>

  <% resource.build_profile %>
  <%= form_for(resource, :as => resource_name,
                         :url => registration_path(resource_name)) do |f| %>
    <%= devise_error_messages! %>

    <h2><%= f.label :email %></h2>
    <p><%= f.text_field :email %></p>

    <h2><%= f.label :password %></h2>
    <p><%= f.password_field :password %></p>

    <h2><%= f.label :password_confirmation %></h2>
    <p><%= f.password_field :password_confirmation %></p>

    <%= f.fields_for :profile do |profile_form| %>
      <h2><%= profile_form.label :first_name %></h2>
      <p><%= profile_form.text_field :first_name %></p>

      <h2><%= profile_form.label :last_name %></h2>
      <p><%= profile_form.text_field :last_name %></p>
    <% end %>

    <p><%= f.submit "Sign up" %></p>

    <br/>
    <%= render :partial => "devise/shared/links" %>
  <% end %>
</div>

And in your User model:

class User < ActiveRecord::Base
  ...
  attr_accessible :email, :password, :password_confirmation, :remember_me, :profile_attributes
  has_one :profile
  accepts_nested_attributes_for :profile
  ...
end
Josien
  • 13,079
  • 5
  • 36
  • 53
mbreining
  • 7,739
  • 2
  • 33
  • 35
  • 3
    wonderfully simple solution, thank you! Just a little consideration: don't you find a bit dirty to build the profile in the view? It's just because we can't edit Devise sigh-up controller easily or may I am too picky? :-) –  Feb 25 '11 at 18:53
  • 2
    No you are not too picky. :) I agree with you, normally you would create a new model instance in the controller, not in the view. But as you mentioned, because the Devise controller would need to be updated, the easiest and simplest solution in this particular case is to do this in the view. – mbreining Feb 27 '11 at 20:34
  • 9
    To complete this answer don't forget to add in your user model: attr_accessible :profile_attributes and accepts_nested_attributes_for :profile (credit here: http://railsforum.com/viewtopic.php?id=42817) – benoitr Apr 25 '11 at 20:34
  • 8
    Actually it should be <% resource.profile || resource.build_profile %>, otherwise you won't get the passed values filled in on form failure. – Papipo Oct 10 '12 at 09:33
  • Just FYI. In Rails 4 `attr_accessible` was extracted out of Rails into a gem and [strong_parameters](http://api.rubyonrails.org/classes/ActionController/StrongParameters.html) is the preferred method. You'll have to add `gem "protected_attributes"` to your Gemfile to use old way. – King'ori Maina Dec 19 '13 at 10:19
8

To complement mbreining's answer, in Rails 4.x you'll need to use strong parameters to allow nested attributes to be stored. Create a registration controller subclass:

RegistrationsController < Devise::RegistrationsController

  def sign_up_params
    devise_parameter_sanitizer.sanitize(:sign_up)
    params.require(:user).permit(:email, :password, profile_attributes: [:first_name, :last_name])
  end
end
ksiomelo
  • 1,878
  • 1
  • 33
  • 38
4

It's not very clear from your question, but I'm assuming your Devise model is User and you created another model Profile that belongs to user.

You'll need to create a controller for your User model with rails g controller users.

You'll also need to generate the views for your users with rails generate devise:views so that the user can add profile info when he's creating his account.

From there, it's just like any other model: create a user and profile instance and link the two. Then, in controllers, use current_user.profile to access the current user's profile.

Note that if you're going to manage users this way, you'll need to remove the :registerable module from the User model (also read https://github.com/plataformatec/devise/wiki/How-To:-Manage-users-through-a-CRUD-interface)

David Sulc
  • 25,946
  • 3
  • 52
  • 54
2

Another way in order not to put building resource in the view is to rewrite the devise controller and to be exact, the new method, all u need to do is to change it to:

 def new
   build_resource({})
   resource.build_profile 
   respond_with self.resource
 end
NoDisplayName
  • 15,246
  • 12
  • 62
  • 98
2

I recommend looking at Creating Profile for Devise users for a more recent answer to the same question and using Rails 4 + Devise

Community
  • 1
  • 1
Stephane Paquet
  • 2,315
  • 27
  • 31