Create a User Profile after saving a Devise User
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
.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).
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.
@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
Hey guys, how's it going? I was wondering, is the #create_profile method a helper or is it a custom method? If it's a custom method, where are you guys adding it? My guess is that it should go on the User model. Any help would be appreciated, thanks a lot.
Hi @pedro these methods are automatically generated by Rails when you set up an association like the belongs_to
so you do not need to create it on any model to make it work with this example.
You can take a look at this reference https://guides.rubyonrails.org/association_basics.html#detailed-association-reference