Reputation: 409
I am new to coding and to stackoverflow, so I hope the question I am about to ask is no too stupid What I am trying to do is in the title: After a user signs in(email, password), they are redirected to a new form where they can fill in their name, bio and pic. Everything goes as planned until they hit submit and bump into 2 issues:
1/ The new data is not taken into account 2/ The redirection is wrong
After sign_up the user is redirected to "profiles/:id/edit". After submit form, it redirects to "user/:id", which doesn't exist.
Shouldn't it redirect to profiles#update in the controller?
Here are my different codes:
1/ routes
Rails.application.routes.draw do
devise_for :users
root to: "pages#home"
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :jam_sessions, only: [:show, :index, :new, :create] do
resources :spots, only: [:show, :create, :new]
resources :messages, only: [:create]
end
resources :participations, only: [:create, :update]
resources :profiles, only: [:show, :edit, :update]
resources :dashboards, only: [:index, :show]
resources :reviews, only: [:create]
mount ActionCable.server => "/cable"
get 'users/:id', :to => 'profiles#edit', :as => :user
patch 'profiles#edit', :to => 'profiles#update'
end
2/ profiles_controller
class ProfilesController < ApplicationController
def show
@user = User.find(params[:id])
@reviews = Review.where(receiver_id: @user.id)
@instruments = UserInstrument.where(user_id: @user.id)
@participations = Participation.where(user_id: @user.id)
date = Time.now
if @participations != nil
@jam_sessions = []
@participations. each do |participation|
spot = Spot.find_by(id: participation.spot_id)
@jam_sessions << JamSession.find_by(id: spot.jam_session_id)
end
@future_jam_sessions = []
@past_jam_sessions = []
@jam_sessions.each do |jam_session|
if jam_session.starts_at > date
@future_jam_sessions << jam_session
else
@past_jam_sessions << jam_session
end
end
else
puts "no jam"
end
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
@user.update(user_params)
raise
if @user.save
redirect_to profiles_path(@user)
else
render "new"
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :bio)
end
end
3/ application_controller
class ApplicationController < ActionController::Base
before_action :authenticate_user!, except: [:home, :index, :show]
before_action :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
# For additional fields in app/views/devise/registrations/new.html.erb
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :bio])
# For additional in app/views/devise/registrations/edit.html.erb
devise_parameter_sanitizer.permit(:account_update, keys: [:username])
end
def default_url_options
{ host: ENV["DOMAIN"] || "localhost:3000" }
end
end
4/ user model
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :user_instruments, dependent: :destroy
has_many :instruments, through: :user_instruments
has_many :messages, dependent: :destroy
has_one_attached :photo, dependent: :destroy
has_many :reviews_written, class_name: "Review", foreign_key: :writer_id
has_many :reviews_received, class_name: "Review", foreign_key: :receiver_id
has_many :jam_sessions, dependent: :destroy
def profile_picture
if photo.attached?
photo.key
else
"avatar-unknown.png"
end
end
def full_name
"#{first_name} #{last_name}"
end
end
5/ edit view
<%= simple_form_for(@user) do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :bio %>
<%= f.input :photo, as: :file %>
<%= f.submit 'Update profile' %>
<% end %>
Thank you! Olivier
Upvotes: 1
Views: 472
Reputation: 2715
it's Clara here :)
So I see some issues with your code, but it also seems like some stuff is already working, which is good. So it is creating a new user and after that redirecting to the right form where you edit the user, am I right? (Just asking because I don't see the redirection from the devise registration form to the user#edit).
In your applications controller, you do have this line, which is unecessary:
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :bio])
You would only need to permit additional parameters for the devise form, if you would add them into the devise registrations form directly. But here you are adding a new form.
So now, how can we handle the new form?
The problem in your code is very subtle. You are changing a user object, but you chose to use a profiles controller
including the profiles routes (But you also have some user routes). The thing is, in the edit user form the following line decides where the HTTP requests goes as soon as someone hits submit.
<%= simple_form_for(@user) do |f| %>
Open the browser and look in the inspect mode which html is generated, it will be something like this (it will have more stuff but this is the interesting part for us)
<form action="/users" accept-charset="UTF-8" method="patch">
This means, when someone hits submit
a HTTP PATCH request is made to /users
Now, the way your routes are build currently, they don't accommodate for this.
So you can add a new routes
resources :users, only [:update]
An into the users_controller#update
you can put the code you have in profiles_controller#update
at the moment.
Now this would leave us with a weird situation where the edit part is in the profiles controller and the update one is in the users controller. You can sove this in two ways
users_controller
but the profiles_controller
and you can keep the set up as you have it. You can do it by adding this line simple_form_for :user, url: user_path, method: "PATCH" ```
Some more remarks now:
In routes.rb
the last line is not correct. And, instead of defining the normal CRUD actions by hand, you should use this syntax:
...
resources :users, only: [:edit, :update]
...
And in the profiles controller
in the update
action and there you need to change the render. (No matter if you leave it there or move it to the users_controller). It should be:
if @user.save
redirect_to profiles_path(@user)
else
render "edit"
end
When the user doesn't get saved, you want to render edit
not new
.
Lastly, there is one downside to creating two different forms but I think it is not a very big problem here: validations.
If you want to have a validation on let's say :bio
it won't work with this setup that you have right now, because the user object already get's created when the devise registration form is submitted. And at this first point the validations would be tested - so you could not test if a bio was there already or not. There are gems to handle this and I also found this article for further research.
https://www.honeybadger.io/blog/multi-step-forms-in-rails/
Upvotes: 2