ROR: Updating two models from one form - ruby-on-rails

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?

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.

how to only edit and destroy my own content only?

Have a basic blog (it's actually edgeguide's blog: http://edgeguides.rubyonrails.org/getting_started.html)
Then I integrated Devise into it. So, user can only log in and see their own information.
Now trying to change it somewhat.
I'd like the users to see all content, but only edit and destroy their own only.
Trying to use before_action filter like this:
`before_action :authorize, :only => [:edit, :destroy]`
And this is the authorize method that I wrote:
def authorize
#article = Article.find(params[:id])
if !#article.user_id = current_user.id then
flash[:notice] = "You are not the creator of this article, therefore you're not permitted to edit or destroy this article"
end
end
But it doesn't work. Everything acts as normal, and I can delete mine and everyone's else content.
How do I get it that I can destroy ONLY my own content, and not everyone's else?
Not using CanCan, nor do I want to.
Not sure if this is worth including or not, but originally when I had everyone see their own content, that was via create action:
def create
#article = Article.new(article_params)
#article.user_id = current_user.id if current_user
if #article.save
redirect_to #article
else
render 'new'
end
end
You're having several problems
first, look at that :
if !#article.user_id = current_user.id then
You're only using one = instead of == so you are doing an assignation that will evaluate to current_user.id
Also, in your condition, you're only setting a flash message but not doing anything to really prevent the user.
Here's a corrected version :
def authorize
#article = Article.find(params[:id])
unless #article.user_id == current_user.id
flash[:notice] = "You are not the creator of this article, therefore you're not permitted to edit or destroy this article"
redirect_to root_path # or anything you prefer
return false # Important to let rails know that the controller should not be executed
end
end

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)

Build an object after user registers in rails

So i'm having this issue trying to figure out how to use the build method in rails to create an object once a user completely registers and still have that object connected to the users id. I'm using devise for authentication and the model that needs to be created is called "app".
This is the create method for "app".
def create
#app = App.new(app_params)
#app.id = current_user.id
respond_to do |format|
if #app.save
format.html { redirect_to #app, notice: 'Application successfully created.'}
else
format.html { render action: 'new' }
end
end
end
Im getting this error:
Couldn't find App with id=1
from my multi step form controller:
def show
#user = User.find(current_user)
case step
when :school, :grades, :extra_activity, :paragraph, :submit
#app = App.find(current_user)
end
render_wizard
end
You need an after_create callback in the User model. It makes no sense to mess with the AppController because no forms have been filled up for the app and you have no app_params.
class User < ActiveRecord::Base
after_create :build_initial_app
protected
def build_initial_app
self.create_app
end
end
You can read more about this at the Rails Guides page for ActiveRecord Callbacks.
The problem line in your code is here:
#app.id = current_user.id
Setting an ActiveRecord object's id is a no-no. Think of the id attribute like you would a pointer in C. The system creates it for you, and you can use it to refer to a unique model object.
What you probably want is something along the lines of:
#app.user_id = current_user.id
Or, even better:
#app.user = current_user
To do that, you need to set up an association between your App model and your User model. There's a good tutorial on that here.

Issue with Rails routes and models

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?

Resources