views:

1148

answers:

3

I'm new to Rails. I'm building an app that has a user model and a profile model.

I want to associate these models such that:
- After the user creates an account, he is automatically sent to the "create profile" page, and the profile he creates is connected to only that particular user.
- Only the user who owns the profile can edit it.

I generated the user model using nifty_generators. When the user hits submit for the account creation, I redirect him to the "new profile" view to create a profile. I did this by editing the redirect path in the user controller. The user controller looks like this:

def create
  @user = User.new(params[:user])
  if @user.save
    session[:user_id] = @user.id
    flash[:notice] = "Thank you for signing up! You are now logged in."
    redirect_to new_profile_path
  else
    render :action => 'new'
  end
end

This was working, but the problem was that the app didn't seem to recognize that the profile was connected to that particular user. I was able to create profiles, but there didn't seem to be a relationship between the profile and the user.

My Profile model lists: belongs_to :user
My User model lists: has _one :profile

My routes.rb file lists the following:
map.resources :users, :has_one => :profile
map.resources :profiles

I have a user_id foreign key in the profiles table. My schema looks like this:

create_table "profiles", :force => true do |t|
  t.integer  "user_id"
  t.string   "name"
  t.string   "address1"
  t.string   "address2"
  t.string   "city"
  t.string   "state"
  t.string   "zip"
  t.string   "phone"
  t.string   "email"
  t.string   "website"
  t.text     "description"
  t.string   "category"
  t.datetime "created_at"
  t.datetime "updated_at"
end

create_table "users", :force => true do |t|
  t.string   "username"
  t.string   "email"
  t.string   "password_hash"
  t.string   "password_salt"
  t.datetime "created_at"
  t.datetime "updated_at"
end

To try to connect the profile to the user, I updated the profiles_controller.rb file with the following, which I basically extrapolated from the Rails Getting Started Guide. My thinking is that in my app, profiles connect to users in the same way that in the Rails Getting Started app, comments connect to posts. Here's the relevant parts of my profiles controller. I can provide the whole thing if it will help:

def new
  @user = User.find(params[:user_id])
  @profile = @user.profile.build
end

def create
  @user = User.find(params[:user_id])
  @profile = @user.profile.build(params[:profile])
  if @profile.save
    flash[:notice] = 'Profile was successfully created.'
    redirect_to(@profile)
  else
    flash[:notice] = 'Error.  Something went wrong.'
    render :action => "new"
  end

end

After making these updates to the profiles controller, now when I submit on the account creation screen, I'm redirected to an error page that says:

ActiveRecord::RecordNotFound in ProfilesController#new
Couldn't find User without an ID

This all seems like a pretty straight-forward Rails use case, but I'm not sure which pieces are wrong. Thanks in advance for your help!

A: 

For usability sake the profile should be part of the user model, or you should create your account and at least the base of your profile all in one go, as a user I i registered and then had to fill in another form I think I would be pretty annoyed.

The upside of this is that either way you can do this with one form. I'd look at the railscasts on complex forms and if you can handle a very modest financial outlay then the pragmatic programmers series on Mastering Rails Forms is a total winner.

railsninja
+4  A: 

When a user is created, the client is redirected to the new action in the ProfileController without an id. You need to explictly pass the user's id in the parameters. It's an idiom to pass the reference to the entire object, not just the id.

# app/controllers/users_controller.rb

def create
  # ...
  redirect_to new_user_profile_path(:user_id => @user)
  # ...
end

Notice that I'm using new_user_profile_path and not new_profile_path. The former is the nested resource that you defined in routes.rb. You should delete map.resources :profiles because a profile cannot exist without a user.

Tate Johnson
Thanks, Tate. I followed your advice, and then encountered some other errors, which I've been trying to work through on my own. I'm now stuck again. When I click submit when creating a user, it directs me to localhost:3000/users/5/profile/new.But I get the trace page that says: NoMethodError in ProfilesController#newYou have a nil object when you didn't expect it!The error occurred while evaluating nil.buildThe trace says it's breaking in the profiles controller - new profile method. def new @user = User.find(params[:user_id]) @profile = @user.profile.build endIdeas?
MikeH
This is a poor route to go down. You should be creating both at once unless you have a really REALLY good reason not too. I refer to my answer.
railsninja
`object.association.build` is for a collection of associations. Singular associations should use `object.build_association` where association is the name of the associated model. If you're in doubt, you should fire up the rails console and experiment with how this worksAs pointed out by @railsninja, you really ought to think about the user experience of completing two forms. None the less, it's a perfectly valid solution for nested associations.
Tate Johnson
A: 

Because user_id doesn't exist or is not created in the profile database. When creating user

asdf