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.

On 2011-10-11, at 11:23 AM, Nikhil G. wrote:

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

On Tue, Oct 11, 2011 at 12:23 PM, Nikhil G.
[email protected]wrote:

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 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

Best Regards,

Everaldo

On Tue, Oct 11, 2011 at 8:15 AM, Peter V.

On Tue, Oct 11, 2011 at 7:38 PM, Nikhil G.
[email protected]wrote:

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
    Active Record Associations — Ruby on Rails Guides
  • actually, I believe what you propose above will not work, it would
    probably be

@user.build_referral # untested

HTH,

Peter

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

Peter V. 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:

Robert W. wrote in post #1026165:

Peter V. 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:

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

On Oct 11, 10:49pm, Peter V. [email protected]
wrote:

  • 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

On Tue, Oct 11, 2011 at 11:52 PM, Robert W.
[email protected]wrote:

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

No prob. In reference to

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

On Oct 17, 12:14am, Peter V. [email protected]
wrote:

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