Create Two objects at the same time

I have user model and referral model. Referral model has user_id as
field.

Now when a new user is created, I need to call referral#create as well
and pass it the id of the newly generated user to user_id of referral
model. How can I do that.

I have user model and referral model. Referral model has user_id as
field.

Now when a new user is created, I need to call referral#create as well
and pass it the id of the newly generated user to user_id of referral
model. How can I do that.

to answer what you said, in your User model, add the following

before_create :generate_referral

private
def generate_referral
  self.referrals.create! {referral_attributes}
end

There are better ways of solving this but I tried to convert your message into code in the most concise way possible

I would suggest to not setting the user_id field in the referral object

manually in your code, but use the built-in mechanisms for:

  • building the objects first in memory

  • saving the object with its associated objects in one “atomic” save.

If you set


class User < ActiveRecord::Base

has_many :referrals

end

You can do this:


$ rails c

Loading development environment (Rails 3.1.1.rc1)

001:0> u = User.new(:name => "Peter")

=> #

002:0> r1 = u.referrals.build(:comment => "sailor")

=> #

003:0> r2 = u.referrals.build(:comment => "developer")

=> #

004:0> u.save

(0.4ms)  BEGIN

SQL (77.3ms)  INSERT INTO "users" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["created_at", Tue, 11 Oct 2011 10:56:14 UTC +00:00], ["name", "Peter"], ["updated_at", Tue, 11 Oct 2011 10:56:14 UTC +00:00]]

SQL (1.0ms)  INSERT INTO "referrals" ("comment", "created_at", "updated_at", "user_id") VALUES ($1, $2, $3, $4) RETURNING "id"  [["comment", "sailor"], ["created_at", Tue, 11 Oct 2011 10:56:14 UTC +00:00], ["updated_at", Tue, 11 Oct 2011 10:56:14 UTC +00:00], ["user_id", 2]]

SQL (0.4ms)  INSERT INTO "referrals" ("comment", "created_at", "updated_at", "user_id") VALUES ($1, $2, $3, $4) RETURNING "id"  [["comment", "developer"], ["created_at", Tue, 11 Oct 2011 10:56:14 UTC +00:00], ["updated_at", Tue, 11 Oct 2011 10:56:14 UTC +00:00], ["user_id", 2]]

(0.9ms)  COMMIT

=> true

005:0> u

=> #

006:0> r1

=> #

007:0> r2

=> #

The magic is that you can add multiple referrals to the user in memory (without ever writing to

the database), and when you are fully done with preparing the user and the referrals, you do

one “atomic” save which will write all object to database (or none if it fails !).

To demonstrate that, I added to the referrals model a validation:


validates :comment,  :presence => true

And make the validation fail:


$ rails c

Loading development environment (Rails 3.1.1.rc1)

001:0> u2 = User.new(:name => "Jan")

=> #

002:0> r1 = u2.referrals.build(:comment => "wanderer")

=> #

003:0> r2 = u2.referrals.build ### no comment here !

=> #

004:0> u2.save

(0.4ms)  BEGIN

(0.4ms)  ROLLBACK

=> false

005:0> User.find_by_name("Jan")

User Load (1.9ms)  SELECT "users".* FROM "users" WHERE "users"."name" = 'Jan' LIMIT 1

=> nil

As you can see, nothing got saved, because I tried all saves in one “atomic” save.

HTH,

Peter

Hi!

Last month I was reading “Rails Antipatterns” and if I understood well, your situation fits with one described in the book:

The author says, in page 150:

“A presenter is simply a plain old Ruby class that orchestrates the creation of multiple models.”

I suggest you look for Active Presenter:

http://jamesgolick.com/2008/7/27/introducing-activepresenter-the-presenter-library-you-already-know.html

https://github.com/jamesgolick/active_presenter

Best Regards,

Everaldo

I had setup the associations, one to one, because that was the
requirement

User Model
has_one :referral

Referral Model
belongs_to :user

and I also setup a "user_id" column in Referral model.

But what I did not know was about the magic call "BUILD"

Now I guess, all I have to do is

@user = User.new(:params[:user])
@user.referral.build
@user.save

Hi Nikhil,

A few hints.

  • there is a preference (at least by me) to reply below the previous text

as I do here (as opposed to “top quoting”)

  • you can find all details about these association methods in section

“4.2 has_one Association Reference” of http://guides.rubyonrails.org/association_basics.html

  • actually, I believe what you propose above will not work, it would probably be

@user.build_referral # untested

HTH,

Peter

Peter Vandenabeele wrote in post #1026116:

* actually, I believe what you propose above will not work, it would
probably be

@user.build_referral # untested

Actually, your believe isn't accurate. You should have tested. :slight_smile:

See collection.build and collection.create explained here:

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many

Robert Walker wrote in post #1026165:

Peter Vandenabeele wrote in post #1026116:

* actually, I believe what you propose above will not work, it would
probably be

@user.build_referral # untested

Actually, your believe isn't accurate. You should have tested. :slight_smile:

See collection.build and collection.create explained here:

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many

Sorry, I just realized that is is a has_one rather than has_many, so
please disregard previous reply.

* you can find all details about these association methods in section
"4.2 has_one Association Reference" ofhttp://guides.rubyonrails.org/association_basics.html
* actually, I believe what you propose above will not work, it would
probably be

@user.build_referral # untested

You were absolutely right, @user.build_referral works, I have tested
it and it has been clearly mentioned in the rubyonrails guides.

@user.referral.build

would have worked only if the association was of the type has_many or
has_and_belongs_to_many

No prob. In reference to

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

I have always been curious why the format of the association methods

is so different for the singular compared to collection associations. This then

leads to confusion as shown above …

has_many => self.others.xxx (many methods added by has_many)

has_one => self.{build|create}_other

Maybe for the singular association, we could add

self.other.build

self.other.create

self.other.create!

to make it more uniform ?

HTH,

Peter

Has_one is very similar to has_many, in sense, but rails does a LIMIT
to 1 when we use has_one. In essence, its a good way to differentiate
that we are using has_one or has_many.

Thats my understanding and opinion.

Regards
Nikhil