I have tried to tidy this post up so it doesnt seem so long for new comers. It includes errors resulting from following suggestions from others in the answers below.
I am trying to make an app with Rails 4.
I use devise and omniauth.
I have models for user and profile. User has one profile and profile belongs to user.
My aim is to use the user model for all the things that the user doesn't really touch. I use the profile model for things the user does maintain.
When a new user is created, I want the user to be redirected to their profile page, which should be partly populated with details created in the user model.
I'm having trouble.
I have:
User.rb
has_one :profile
Profile.rb
belongs_to :user
Omniauth_callbacks_controller.rb
if @user.persisted?
sign_in_and_redirect_user(:user, authentication.user.profile)
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
I have also tried in the call back redirect:
sign_in_and_redirect @user, event: :authentication
profiles/show.html.erb
<div class="container-fluid">
<div class="row">
<div class="col-xs 8 col-xs-offset-1">
<%= @profile.user.formal_name %>
<%= @profile.occupation %>
<%= @profile.qualification.awards %>
<%= @profile.overview %>
<%= @profile.research_interests %>
</div>
<div class="col-xs 3">
<div class="geobox">
<%= render 'addresses/location' %>
<%= @profile.working_languages %>
<%= @profile.external_profile %>
</div>
</div>
</div>
<div class="row">
<div class="col-xs 10 col-xs-offset-1">
<%= render 'profiles/activity' %>
</div>
</div>
</div>
_nav.html.erb
<% if user_signed_in? %>
Hi <%= link_to(current_user.first_name.titlecase, profile_path(current_user)) %></span>
I have also tried:
_nav.html.erb
<% if user_signed_in? %>
Hi <%= link_to(current_user.first_name.titlecase, user_profile_path(current_user.profile)) %></span>
What do I need to do to add some kind of functionality to let a new user go to their profile page?
Currently, when I try logging in and clicking on the nav bar link, I want to go to the profile page. Instead, I go to an error message which says the page I am looking for does not exist. The path it is looking for starts at profile (rather than user/profile) -not sure if that's a clue.
My routes are:
Profiles:
profiles GET /profiles(.:format) profiles#index
POST /profiles(.:format) profiles#create
new_profile GET /profiles/new(.:format) profiles#new
edit_profile GET /profiles/:id/edit(.:format) profiles#edit
profile GET /profiles/:id(.:format) profiles#show
PATCH /profiles/:id(.:format) profiles#update
PUT /profiles/:id(.:format) profiles#update
DELETE /profiles/:id(.:format) profiles#destroy
User:
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
cancel_user_registration GET /users/cancel(.:format) users/registrations#cancel
user_registration POST /users(.:format) users/registrations#create
new_user_registration GET /users/sign_up(.:format) users/registrations#new
edit_user_registration GET /users/edit(.:format) users/registrations#edit
PATCH /users(.:format) users/registrations#update
PUT /users(.:format) users/registrations#update
DELETE /users(.:format) users/registrations#destroy
user_confirmation POST /users/confirmation(.:format) devise/confirmations#create
new_user_confirmation GET /users/confirmation/new(.:format) devise/confirmations#new
GET /users/confirmation(.:format) devise/confirmations#show
user_unlock POST /users/unlock(.:format) devise/unlocks#create
new_user_unlock GET /users/unlock/new(.:format) devise/unlocks#new
GET /users/unlock(.:format) devise/unlocks#show
finish_signup GET|PATCH /users/:id/finish_signup(.:format) users#finish_signup
NEW ATTEMPT: Further to the suggestion below, I nested the resources as:
resources :users do
resources :profile
end
and I changed the redirect to:
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
@user = User.find_for_oauth(env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect [current_user, current_user.profile || current_user.build_profile]
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
I get this error: NoMethodError (undefined method `profile' for nil:NilClass):
NEXT ATTEMPT:
I keep all of the above, but try replacing the redirect like so:
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
@user = User.find_for_oauth(env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect [@user, @user.profile || @user.build_profile]
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
RuntimeError (Could not find a valid mapping for [#<User id: 1 ...
and then it lists all the attributes in the user table, and then shows:
#<Profile id: nil, user_id: 1,
followed by all the attributes in the profiles table.
A NEW ATTEMPT
I found this post: Rails Trying to create a profile after the user has been created
And I tried adding:
after_create do
create_user_profile
end
to my user model.
I kept the redirect the same as I had it (following Alexei's suggestion) and tried again.
I get the same error message:
RuntimeError (Could not find a valid mapping for [#<User id: 1
Can anyone see how to solve this?
A FURTHER ATTEMPT:
I then found this post: Ruby on Rails - Creating a profile when user is created
which says for Rails 4 (which I use), I need this callback only:
after_create :create_profile
When I try this, I get the same error as above.
AN IDEA:
I used scaffolding generator for my profile controller.
My create method is:
def create
@profile = Profile.new(profile_params)
respond_to do |format|
if @profile.save
format.html { redirect_to @profile }
format.json { render :show, status: :created, location: @profile }
else
format.html { render :new }
format.json { render json: @profile.errors, status: :unprocessable_entity }
end
end
end
Is this error maybe something to do with the user id not being specifically referenced in this method. I have whitelisted user_id in the strong params for the profiles controller.
A NEW ATTEMPT
This article suggests using build_profile instead of create_profile.
https://stackoverflow.com/questions/8337682/create-another-model-upon-user-new-registration-in-devise
I changed my user.rb to
after_create :build_profile
When I save this and try I get the same error.
ANOTHER ATTEMPT
Matt's answer in this post suggests that I have to include a definition of build_profile as:
after_create :build_profile
def build_profile
Profile.create(user: self) # Associations must be defined correctly for this syntax, avoids using ID's directly.
end
I changed my user.rb to:
after_create :build_profile
def build_profile
Profile.create(user: self) # Associations must be defined correctly for this syntax, avoids using ID's directly.
end
When I save and try this, I get a similar error, but with more information. The logs still show:
RuntimeError (Could not find a valid mapping for [#<User id: 1,
But after all of that error message, it gives this new piece of information:
SQL (2.9ms) INSERT INTO "profiles" ("user_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["user_id", 1], ["created_at", "2015-12-18 21:58:49.993866"], ["updated_at", "2015-12-18 21:58:49.993866"]]
2015-12-18T21:58:49.966648+00:00 app[web.1]: Profile Load (1.5ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = $1 LIMIT 1 [["user_id", 1]]
2015-12-18T21:58:50.007428+00:00 app[web.1]: Completed 500 Internal Server Error in 113ms (ActiveRecord: 21.8ms)
2015-12-18T21:58:49.941385+00:00 app[web.1]: User Load (1.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
Does this offer any light on the problem?
UPDATE
I tried this again (without making any changes to the code). Now I get an error, but this time the logs say:
RuntimeError (Could not find a valid mapping for [#<User id: 1, first_name: "Me", last_name: "Ma", email: "[email protected]", encrypted_password: "$2a$jqQn..HSvlcNtfAH/28.DQoWetO...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 16, current_sign_in_at: "2015-12-15 09:57:14", last_sign_in_at: "2015-12-15 09:27:07", current_sign_in_ip: #<IPAddr: IPv4:49.191.55>, last_sign_in_ip: #<IPAddr: IPv4:49.191.135.255.255>, confirmation_token: nil, confirmed_at: "2015-12-15 09:02:58", confirmation_sent_at: "2015-12-15 08:42:48", unconfirmed_email: nil, failed_attempts: 0, unlock_token: nil, locked_at: nil, created_at: "2015-12-09 23:53:20", updated_at: "2015-12-15 09:57:14", image: nil>, #<Profile id: 1, user_id: 1, title: nil, hero_image: nil, overview: nil, occupation: nil, external_profile: nil, working_languages: nil, created_at: "2015-12-18 21:58:49", updated_at: "2015-12-18 21:58:49">]):
The difference this time is that the profile has a user id key of 1. Previously, this was nil.
You can use nested resources
resources :users do
resource :profile
end
and then
redirect_to [current_user, current_user.profile || current_user.build_profile]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With