Issue with Rails routes and models - ruby-on-rails

I have 2 models - User model and Profile model. I have setup the relationship as follows:
class User
has_one :profile
end
class Profile
belongs_to :user
end
I have a profiles controller with 4 actions - new create edit and update. Once the User signs up or logs in he is redirected to the New action in the Profiles controller. From here how do I create a profile for that user? Specifically what should I have in my New action and Create action. Right now the route for the new action is just profiles/new which doesn't capture the Users params. I am trying to do this but its failing.
Profiles Controller
def new
#user = User.find(params[:id])
#profile = #user.build_profile
end
def create
#profile = current_user.build_profile(params[:profile])
if #profile.save
redirect_to current_user
else
render new
end
end

the new action in the profile controller doesn't need to get the id of the user from the params.
So your controller would be like this
def new
#user = current_user
#profile = #user.build_profile
end
def create
#profile = current_user.build_profile(params[:profile])
if #profile.save
redirect_to current_user
else
render new
end
end
actually sending the id of the user to the new action could be a security hole as I could send the id of another user and create a profile for some other user in the system, which shouldn't be allowed.

You should not be using User.find(params[:id] in your new action.
Just like in your create action below, you should be getting the User through current_user.
Is there more to the problem than not being able to properly fetch the User?

Related

Ruby on Rails - Unable to create a new profile for each user

I'm building an events app with users who will each have a personal profile. I've set up a few users for the site but when I try and create and/or edit a profile for each user it refers me back to a flash message "That profile doesn't belong to you!" which is in reference to my first user profile which was set up and works fine.
I'm using Devise gem for initial set up but have built out from their with my own user controller. Here's the code from that controller -
class UsersController < ApplicationController
before_action :authenticate_user!
before_action :set_user
before_action :owned_profile, only: [:edit, :update]
def new
#user = User.new
end
def show
#user = User.find(params[:id])
end
def create
end
def edit
#user = current_user #User.find_by(params[:id])
end
def update
#user = User.find_by(params[:id])
if #user.update(user_params)
redirect_to user_path, notice: "Profile successfully updated!"
else
render 'edit'
end
end
private
def user_params
params.require(:user).
permit(:name, :username, :biography, :email, :url)
end
def owned_profile
unless current_user == #user
flash[:alert] = "That profile doesn't belong to you!"
redirect_to root_path
end
end
def set_user
#user = User.find_by(params[:id])
end
end
Any assistance would be appreciated.
I would create an admin. An easy way to do this is to add a column to your users table called admin and make it a boolean. Migrate the db.
Then check to whether a user is an admin before running the owned_profile method. In that method, change: unless current_user == #user to
unless current_user == #user || current_user.admin
Then set yourself as an admin in the console, save and then freely add profiles without that callback running.
If the issue is that Users are not able to edit their own profile, then I believe it is caused by the use of find_by within set_user:
#user = User.find_by(params[:id])
Should be:
#user = User.find(params[:id])
If you truly wanted to use find_by you could do:
#user = User.find_by_id(params[:id])
Or
#user = User.find_by(id: params[:id])
Find_by used as the 2 examples above will not throw an error if a User is not found, while find will.
Sidenote: You can remove the #user assignment within the show action.
You can do it by this way.
When user signing up, automatically creates profile. Good point of this ID of user and profile tables will be the same.
rails g model profile first_name last_name email
rails g migration add_user_id_to_profiles user_id:integer
Profile.rb
belongs_to :user
User.rb
has_one :profile, dependent: :destroy
before_create :set_profile
def set_profile
build_profile(id: self.id, user_id: self.id, email: self.email)
end
GoodLuck.

Redirecting Users in rails

I have a profiles controller where user are allowed to access their own profile, but not others users profile.
When I accessing an url like : http://localhost:3000/en/profiles/2. I want my user to be redirected to their own profile if the URL correspond to disown profile. How I can handle this ?
My actual show action in my profiles controller looks like :
def show
#profile = Profile.find(params[:id])
#user = current_user
#profile = #user.profile
end
I have already try this method but doesn't work
def correct_user
redirect_to(profile_path) unless current_user == (#profile.user if #profile)
end
You should probably move the logic to a before_action.
before_action :find_profile, :enforce_current_profile
def show
end
protected
def find_profile
#profile = Profile.find(params[:id])
end
def enforce_current_profile
unless #profile && #profile.user == current_user
redirect_to(profile_path)
end
end
However, what you really want to do, is to convert the profile controller to a resource rather than a resources in your route file.
resource :profile
In this way, Rails will generate
GET /profile
rather than
GET /profile/2
and you will not need any control. Just set
def show
#user = current_user
#profile = #user.profile
end
Using a resource instead of a resources will not provide the index action, but I doubt you need it.

How to send data from one model/ controller to another in Rails

Not sure how to achieve this:
User enters email on page 1
On page 2, User doesn't have to enter email again, because it has persisted (again across controllers/ models)
Yet another way of saying it using code snippets below:
User enters email "test#gmail.com" in signups/new
Email is posted to signups/create, so now the model signups has one new row of data with "test#gmail.com"
User redirected to requests/new
On requests/new, I'd like the same email that the user just signed up with to be immediately accessible, i.e., #requestrecord.email = "test#gmail.com" in the new action, so User doesn't have to re-enter it and post to requests/create
Signup Controller code
def new
#signup = Signup.new
end
def create
#signup = Signup.new(signup_params)
if #signup.save
redirect_to new_request_path
else
render new_signup_path
end
end
Request Controller code
def new
#requestrecord = Request.new
end
def create
#requestrecord = Request.new(request_params)
end
You could just set the user's email in a session variable:
def create
#signup = Signup.new(signup_params)
if #signup.save
session[:user_email] = #signup.email
redirect_to new_request_path
else
render new_signup_path
end
end
and then you can use it as needed with session[:user_email] in your Request controller.

Ruby on Rails - Creating a profile when user is created

So basically I have wrote my own authentication instead of using a gem so I have access to the controllers. My user creation works fine but when my users are created I want to also create a profile record for them in my profile model. I have got it mostly working I just cant seem to pass the ID from the new user into the the new profile.user_id. Here is my code for the user creation in my user model.
def create
#user = User.new(user_params)
if #user.save
#profile = Profile.create
profile.user_id = #user.id
redirect_to root_url, :notice => "You have succesfully signed up!"
else
render "new"
end
The profile is creating it is just not adding a user_id from the newly created user. If anyone could help it would be appreciated.
You should really do this as a callback in the user model:
User
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
end
This will now always create a profile for a newly created user.
Your controller then gets simplified to:
def create
#user = User.new(user_params)
if #user.save
redirect_to root_url, :notice => "You have succesfully signed up!"
else
render "new"
end
end
This is now much easier in Rails 4.
You only need to add the following line to your user model:
after_create :create_profile
And watch how rails automagically creates a profile for the user.
You have two errors here:
#profile = Profile.create
profile.user_id = #user.id
The second line should be:
#profile.user_id = #user.id
The first line creates the profile and your are not 're-saving' after the assignment of the user_id.
Change these lines to this:
#profile = Profile.create(user_id: #user.id)

ROR: Updating two models from one form

I have two models profiles and users on my form. After a user is created he can then move to editing his profile. The views work well. But when I click Save to update the editted profile. It doesn't update, but the flash notice displays that profile has been updated. What might be wrong? I'm not sure what went wrong. Below is the code.
class ProfilesController < ApplicationController
def new
##user.profile = Profile.new
#user = User.find(current_user.id)
#identity = #user.profile || #user.build_profile()
#identity.save
end
def update
#user = current_user
#identity = #user.profile
if #identity.update_attributes(params[:identity])
flash[:notice] = 'Profile was successfully updated.'
redirect_to(new_profile_path())
else
render :action => "new"
end
end
end
class UsersController < ApplicationController
def show
#user = current_user
#user = User.find(params[:id])
#identity = #user.profile || #user.build_profile()
#identity.save
end
......
end
Thanks for your assistance.
There are potentially a few things wrong here. But the best solution to this problem would be to simplify and use the built in rails features for editing associations.
What I suggest doing is using nested attributes, Ryan Daigle has a great article on them.
I'm not sure why you're calling save in a new action and not in a create, that doesn't feel right. Also check that the name of the model in the form you're submitting is identity and not user or profile.
Can a user exist without a profile?

Resources