All threads / Create a User Profile after saving a Devise User

Ask A Question

Notifications

You’re not receiving notifications from this thread.

Create a User Profile after saving a Devise User

Jason Brown asked in Gems / Libraries
  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
``

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!

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

This is awesome, but is there a way to do it, get a profile.id and still be able to do something like this later:

# profile.rb
validates_presence_of :name, :bio

You could do something like this:

class User < ApplicationRecord
  has_one :profile
  after_create :init_profile

  def init_profile
    self.build_profile.save(validate: false)
  end
end

This lets you create the records but if you have other functions that check if profile.valid? then it would return false, and this may not be the desired result. See: https://www.dan-manges.com/blog/action-dependent-validations-and-why-on-update-is-bad Notice they're talking about the on: :update, which isn't what we're doing here, however the side effect is the same.

how would you create an edit page with a form to edit the user profile

@foliwe

how would you create an edit page with a form to edit the user profile

Assuming you are using devise and your models look something like this:

class Profile < ApplicationRecord
  belongs_to :user
end

# and 

class User < ApplicationRecord
  has_one :profile, dependent: :destroy
  after_create :init_profile

  def init_profile
    self.create_profile!
  end
end

Then in your profiles_controller.rb:

  def edit
      @profile = current_user.profile
  end

  def update
      @profile = current_user.profile
    respond_to do |format|
      if @profile.update profile_params
        format.html { redirect_to edit_profile_path, notice: "Profile updated!" }
        format.json { render :edit, status: :ok, location: @profile }
      else
        format.html { redirect_to edit_profile_path, flash: { error: "Profile could not be updated!" } }
        format.json { render json: @profile.errors.messages, status: :unprocessable_entity }
      end
    end
  end

then in views/profiles/edit.erb:

<%= form_for @profile, url: {action: "update"} do |f| %>

  <!-- Whatever your fields are -->
  <%= f.text_field :name %>
  <%= f.text_area :bio %>

  <%= f.submit "Update Profile" %>
<% end %>

Using @profile = current_user.profile instead of @profile = Profile.find(params[:id]) means that you always get the currently logged in user's profile when they go to an edit view, so they can't get anyone else's. It also sets up the form to already know which user's profile to update. If anyone has a reason not to do this, let me know!

Is it possible to take away the sign_up option and set the users fix in the back end?
I dont want others to have the possibility to sign up at my website, except i create the user in the database. I know there is something like activeadmin, but i think that is to complex for what i want to achieve

Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 35,699+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2020 GoRails, LLC. All rights reserved.