Skip to main content

Create a User Profile after saving a Devise User

Gems / Libraries • Asked by Jason Brown


I want to have a user profile instance created in the database which is associated with a newly created devise user. The profile should be created only once and have a user_id associated with the newly created devise user. A few things are confusing me..

  1. The action should happen atomicly. Either the whole things succeeds or the whole thing fails. Orphaned profiles are users created without profiles wouldn't be good. How to do this?

  2. At the time devise creates a user, the current_user variable isn't available as the user is not logged in. Configuring devise to login automatically after account creation could work but what if I change my mind and don't want this in the future. So I need a way to associate a user_id with a profile w/o using the current_user so my app can stay flexible.

  3. Is there a better approach that I'm missing? I'm looking for the correct solution instead of just a solution. Toying around with the user_model the wrong way feels dangerous and I want to do the right thing. I'd really enjoy hearing your opinions how best to do this in a way that doesn't go against the RoR framework and keeps things clear, flexible and maintainable.

Gravatar Chris Oliver commented on : Mod Staff
  1. When the user registers, do you want to have them fill out some of the profile information as well? If so, then I would recommend updating the Devise form to be a nested form. That way you create both together at once using accepts_nested_attributes_for.

  2. When Devise creates a user, it does actually sign them in as soon as they're persisted to the database (it would be weird if you registered and then had to login immediately).

  3. Depends on what you want to accomplish really. If you want two things filled out at once, the above solution in #1 is great and pretty painless.

The other option is just to create a blank profile during the registration process if that's what you're looking for. In this case, I would override the Devise controller, copy the create action, and then add in code on the save part to also create and save your profile for the resource.

For example, you could modify the default create action to look like this which would provide an atomic method of creating both the user and the profile:

class Devise::RegistrationsController
  def create
    build_resource(sign_up_params)

    resource.save
    yield resource if block_given?
    if resource.persisted?

      # We know that the user has been persisted to the database, so now we can create our empty profile
      resource.create_profile!

      if resource.active_for_authentication?
        set_flash_message! :notice, :signed_up
        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}"
        expire_data_after_sign_in!
        respond_with resource, location: after_inactive_sign_up_path_for(resource)
      end
    else
      clean_up_passwords resource
      set_minimum_password_length
      respond_with resource
    end
  end
end
``

I changed this in config/routes.rb

devise_for :users,
             controllers: { registrations: "devise_override/registrations" }

Then did an override of the Devise create and destroy actions

class DeviseOverride::RegistrationsController < Devise::RegistrationsController
  def create
    super # this calls Devise::RegistrationsController#create as usual
    # after creating a new user, create a profile that has
    # the profile.user_id field set to the user_id of the user jsut created
    if resource.save
        @profile = Profile.new
        @profile.user_id = resource.id
        @profile.save
    end
  end

  def destroy
    super
    if resource.destroy
        @profile = Profile.find(user_id: resource.id)
        @profile.destroy
    end
  end
end

Is there a downside to this approach?

Gravatar Chris Oliver commented on : Mod Staff

Looks pretty good. I think if you have the has_one and belongs_to associations setup on User and profile, I would modify your create like this:

    if resource.save
        resource.create_profile
    end

That'll be a little cleaner and easier to maintain.

For the destroy, it might make more sense to use a dependent: :destroy callback on the User models: has_one :profile, dependent: :destroy. This way anytime you destroy a user, you can also destroy their profile. It will happen automatically.

Hello @JasonBrown - I'm trying to do exactly what you described. Did your solution work? Any example code you care to share? Thanks!

Yes, it worked fine but I wouldn't do it again. I'd use lazy instantiation instead. Create the user profile only when the user tries to access it but it doesn't yet exist. Another approach, just add fields to the devise user model and be more specific in your queries (in other words don't SELECT * ) so you aren't caching or loading all the user profile information with each request. Either way works. I'd go with lazy instantiation personally.

Unless I'm missing something, I think this is the simplest way and doesn't require modifying your devise controller:

class User < ActiveRecord::Base

  has_one :profile, dependent: :destroy
  after_create :init_profile

  def init_profile
    self.create_profile!
  end

end

Even better, Thank you Jacob. I'll fire up a toy project and write some tests just to make sure it works.

Login or create an account to join the conversation.