106

Hi (huge Rails newbie here), I have the following models:

class Shop < ActiveRecord::Base
  belongs_to :user
  validates_uniqueness_of :title, :user_id, :message => "is already being used"
end

and

class User < ActiveRecord::Base
  has_one :shop, :dependent => :destroy
end

When I'm about to create a new shop, I get the following error:

private method `create' called for nil:NilClass

This is my controller:

@user = current_user
@shop = @user.shop.create(params[:shop])

I've tried different variations by reading guides and tutorials here and there, but I'm more confused than before and can't get it to work. Any help would be greatly appreciated.

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
Neko
  • 1,099
  • 2
  • 7
  • 7
  • Edited question title to reflect question. Duplicate of [Using build with a has_one association in rails](http://stackoverflow.com/questions/2472982/using-build-with-a-has-one-association-in-rails) – Marc-André Lafortune May 09 '12 at 18:53
  • 1
    you can also use `@user.build_shop(params) ` – Mani Jun 04 '16 at 05:58

4 Answers4

250

A more concise way to do this is with:

@user.create_shop(params[:shop])

See methods added by has_one in the Ruby on Rails guides.

nates
  • 8,312
  • 5
  • 32
  • 28
  • 7
    This is definitely more better approach – Grey Mar 23 '16 at 14:20
  • 10
    Beware that if you create_shop more than once that it will delete the previous shop. For instance if you run `@user.create_shop(params[:shop_one_info])` it will create shop_one, BUT if you run `@user.create_shop(params[:shop_two_info])` that it will delete the first shop and create the second one. – ecoding5 May 06 '16 at 17:08
  • The above comment about deleting the previous shop is for Rails 3.2.18, don't know about more recent versions. Can't edit comment after 5 min -_- – ecoding5 May 06 '16 at 17:15
  • Found a solution, I did not set uniqueness on the associated model, so make sure you do as how it is set up in this example's Shop model. – ecoding5 May 06 '16 at 17:22
  • you can alse use `@user.build_shop(params)` – Mani Jun 04 '16 at 05:57
134

First of all, here is how to do what you want:

@user = current_user
@shop = Shop.create(params[:shop])
@user.shop = @shop

Now here's why your version did not work:

You probably thought that this might work because if User had a has_many relation to Shop, @user.shops.create(params[:shop]) would work. However there is a big difference between has_many relations and has_one relations:

With a has_many relation, shops returns an ActiveRecord collection object, which has methods that you can use to add and remove shops to/from a user. One of those methods is create, which creates a new shop and adds it to the user.

With a has_one relation, you don't get back such a collection object, but simply the Shop object that belongs to the user - or nil if the user doesn't have a shop yet. Since neither Shop objects nor nil have a create method, you can't use create this way with has_one relations.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • Thanks for your answer, sepp2k. I see now why my code couldn't work. – Neko Oct 01 '10 at 14:21
  • 133
    You could also use `@user.create_shop(params[:shop])`. See [methods added by has_one](http://guides.rubyonrails.org/association_basics.html#methods-added-by-has_one). – nates Apr 08 '13 at 21:01
  • The answer chosen works, but @nates solution works also. +1 to both of you. – nfriend21 Jul 02 '13 at 01:38
  • +1 to the answer because I was wondering the same, +1 to the answer for explaining why this is and +1 to the comment for giving the best solution. – deivid Jul 03 '13 at 16:54
  • The distinction between the return values (the collection with `has_many` vs the object or nil from `has_one`) was a super helpful distinction. Thank you! – dtluther Jul 01 '22 at 21:55
7

Two more ways if you want save instead of create:

shop = @user.build_shop
shop.save

shop = Show.new
shop.user = @user
shop.save
Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232
3

Just to add to above answers -

@user.create_shop(params[:shop])

Above syntax creates new record but it subsequently deletes similar existing record.

Alternatively, if you do not want to trigger delete callback

Shop.create(user_id: user.id, title: 'Some unique title')

This thread might be helpful. Click here

Rais
  • 69
  • 5