One-to-one relationship

Is it wrong to use a beings_to on both side of a one-to-one
association ?

User
belongs_to :account so I have an account_id field

Account
belongs_to :owner, :class_name => ‘User’, :foreign_key => ‘user_id’

I can get user.account and account.owner
It runs, but I wonder about any collateral effect…

thanks for your feedback

I think it’s fine.

Best Regards,
Everaldo

On Dec 1, 2011, at 5:01 PM, Everaldo G. wrote:

belongs_to :account so I have an account_id field

Account
belongs_to :owner, :class_name => ‘User’, :foreign_key => ‘user_id’

I can get user.account and account.owner
It runs, but I wonder about any collateral effect…

thanks for your feedback

Which one do you create first? What foreign key value does it get? Do
you always do the create/create/update in a transaction?

Do you ever (ever!) have one without the other?

class User < ActiveRecord::Base
has_one :account
end

class Account < ActiveRecord::Base
belongs_to :owner, :class_name => ‘User’, :foreign_key => ‘user_id’
end

I think that current versions of ActiveRecord have the right default
for the foreign key (which is the _id after the class name rather than
the association name), but specifying it works just fine, of course.

-Rob

Rob B.
[email protected] http://AgileConsultingLLC.com/
[email protected] http://GaslightSoftware.com/

On Thu, Dec 1, 2011 at 10:59 PM, Erwin [email protected] wrote:

It runs, but I wonder about any collateral effect…

I don’t like it (if I understand correctly that you have both
in users table account_id
in accounts table user_id)

  1. This code expresses the 1-on-1 link between
    a user and his associated account 2 times
    (user.account_id points to account.id and
    account.user_id point to user.id).

So, there is a chance that the 2 links go out of sync.

  1. You will always need 3 database writes to save
    a pair of a user and an account.

If you did:

class User
belongs_to :account
end

class Account
has_one :user
end

you could write

account = Account.new(params[:account])
user = account.build_user(params[:user])
account.save # handle the return value

and this will do all that is required (with 2 database writes,
first the account, which will render the account.id and then
user will will be saved with user.account_id = account.id).

But with the original code (with 2 belongs_to associations),
I think you will need to do 3 writes:

  • account.save (#=> account.id)
  • user.save (with user.account_id = account.id)
  • a second account.save (for updating account.user_id = user.id)

I fail to see the advantage over a symmetric belongs_to , has_one
relationship.

Also check out the advantages of the inverse_of

class User
belongs_to :account, :inverse_of => :user
end

class Account
has_one :user, :inverse_of => :account
end

This will make sure the user.account is known before the save.

With the :inverse_of, this is the case:

$ rails c
Loading development environment (Rails 3.1.1)
001:0> u = User.new(:name => “peter”)
=> #<User id: nil, name: “peter”, account_id: nil, created_at: nil,
updated_at: nil>
002:0> a = u.build_account(:number => “123”)
=> #<Account id: nil, number: “123”, created_at: nil, updated_at: nil>
003:0> a.user
=> #<User id: nil, name: “peter”, account_id: nil, created_at: nil,
updated_at: nil>
004:0> u.save
(0.4ms) BEGIN
SQL (50.1ms) INSERT INTO “accounts” (“created_at”, “number”,
“updated_at”) VALUES ($1, $2, $3) RETURNING “id” [[“created_at”, Thu,
01
Dec 2011 22:24:16 UTC +00:00], [“number”, “123”], [“updated_at”, Thu, 01
Dec 2011 22:24:16 UTC +00:00]]
SQL (1.6ms) INSERT INTO “users” (“account_id”, “created_at”, “name”,
“updated_at”) VALUES ($1, $2, $3, $4) RETURNING “id” [[“account_id”,
1],
[“created_at”, Thu, 01 Dec 2011 22:24:16 UTC +00:00], [“name”, “peter”],
[“updated_at”, Thu, 01 Dec 2011 22:24:16 UTC +00:00]]
(0.9ms) COMMIT
=> true

Without the inverse_of relations, this is the result:

$ rails c
Loading development environment (Rails 3.1.1)
001:0> u = User.new(:name => “peter”)
=> #<User id: nil, name: “peter”, account_id: nil, created_at: nil,
updated_at: nil>
002:0> a = u.build_account(:number => “123”)
=> #<Account id: nil, number: “123”, created_at: nil, updated_at: nil>
003:0> a.user
=> nil
004:0> # the “back link is only known AFTER the save to db and a reload
:-/”
005:0* ^C
005:0> u.save
(0.4ms) BEGIN
SQL (16.1ms) INSERT INTO “accounts” (“created_at”, “number”,
“updated_at”) VALUES ($1, $2, $3) RETURNING “id” [[“created_at”, Thu,
01
Dec 2011 22:28:35 UTC +00:00], [“number”, “123”], [“updated_at”, Thu, 01
Dec 2011 22:28:35 UTC +00:00]]
SQL (1.3ms) INSERT INTO “users” (“account_id”, “created_at”, “name”,
“updated_at”) VALUES ($1, $2, $3, $4) RETURNING “id” [[“account_id”,
2],
[“created_at”, Thu, 01 Dec 2011 22:28:35 UTC +00:00], [“name”, “peter”],
[“updated_at”, Thu, 01 Dec 2011 22:28:35 UTC +00:00]]
(0.8ms) COMMIT
=> true
006:0> a.user
=> nil
007:0> a.reload
Account Load (1.4ms) SELECT “accounts”.* FROM “accounts” WHERE
“accounts”.“id” = $1 LIMIT 1 [[“id”, 2]]
=> #<Account id: 2, number: “123”, created_at: “2011-12-01 22:28:35”,
updated_at: “2011-12-01 22:28:35”>
008:0> a.user
User Load (1.0ms) SELECT “users”.* FROM “users” WHERE
“users”.“account_id” = 2 LIMIT 1
=> #<User id: 2, name: “peter”, account_id: 2, created_at: “2011-12-01
22:28:35”, updated_at: “2011-12-01 22:28:35”>
009:0> # work-around (when inverse_of is not available)
010:0* ^C
010:0> u3 = User.new(:name => “peter”)
=> #<User id: nil, name: “peter”, account_id: nil, created_at: nil,
updated_at: nil>
011:0> a3 = u3.build_account(:number => ‘456’, :user => u3)
(0.4ms) BEGIN
(0.3ms) COMMIT
=> #<Account id: nil, number: “456”, created_at: nil, updated_at: nil>
012:0> a3.user
=> #<User id: nil, name: “peter”, account_id: nil, created_at: nil,
updated_at: nil>
013:0>

HTH,

Peter

Kad K. wrote in post #1034622:

Is it wrong to use a beings_to on both side of a one-to-one
association ?

User
belongs_to :account so I have an account_id field

Account
belongs_to :owner, :class_name => ‘User’, :foreign_key => ‘user_id’

I can get user.account and account.owner
It runs, but I wonder about any collateral effect…

thanks for your feedback

belongs_to should be on the side of a one-to-one association that
contains the foreign key (same as a one-to-many).

The other side (the side without a foreign key should use has_one NOT
belongs to.

User
has_one :account

Account
belongs_to :owner, :class_name => ‘User’, :foreign_key => ‘user_id’

Or you could use the standard conventions:

Account
belongs_to :user

On Thu, Dec 1, 2011 at 8:35 PM, Robert W. [email protected]
wrote:

I can get user.account and account.owner
I think I didn’t thought enough before reply. =)
Good arguments, guys!

Thanks … it runs , but as mentioned it has some side effects…
even if I cannot have one side wo the other one …
learn a lot about collateral effects…

Thanks … it runs , but as mentioned it has some side effects…
even if I cannot have one side wo the other one …
learn a lot about collateral effects…

Thanks … it runs , but as mentioned it has some side effects…
even if I cannot have one side wo the other one …
learn a lot about collateral effects…

On Dec 1, 11:29pm, Rob B. [email protected]

Got a clear understanding on what to do and not to do even if it
works …

Thanks … it runs , but as mentioned it has some side effects…
even if I cannot have one side wo the other one …
learn a lot about collateral effects…