Ruby on Rails
Thursday, December 1, 2011
On Thu, Dec 1, 2011 at 10:59 PM, Erwin <yves_dufour@mac.com> wrote:
-- 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...
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.
2) 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
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
To post to this group, send email to rubyonrails-talk@googlegroups.com.
To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment