Apparently I assumed functionality that doesn''t exist for this association. Should I not be able to reference the target model as an attribute? Here are my models: class User < ActiveRecord::Base has_one :membership has_one :role, :through => :membership end class Membership < ActiveRecord::Base belongs_to :user belongs_to :role end class Role < ActiveRecord::Base end But, the "role" attribute for an instance of User always contains nil:>> user = User.new=> #<User id: nil, created_at: nil, updated_at: nil>>> user.save!=> true>> membership = user.create_membership=> #<Membership id: 1, user_id: 1, role_id: nil, created_at: "2009-05-20 05:40:1 7", updated_at: "2009-05-20 05:40:17">>> role = membership.create_role=> #<Role id: 1, created_at: "2009-05-20 05:40:33", updated_at: "2009-05-20 05:4 0:33">>> user.membership=> #<Membership id: 1, user_id: 1, role_id: 1, created_at: "2009-05-20 05:40:17" , updated_at: "2009-05-20 05:40:17">>> user.membership.role=> #<Role id: 1, created_at: "2009-05-20 05:40:33", updated_at: "2009-05-20 05:4 0:33">>> user.role=> nil>> quitWhy does user.membership.role contain a record, but user.role does not?
Brian wrote: [...]> class User < ActiveRecord::Base > has_one :membership > has_one :role, :through => :membership > end > > class Membership < ActiveRecord::Base > belongs_to :user > belongs_to :role > end > > class Role < ActiveRecord::Base > end[...] I don''t know if this is the problem, but I think you forgot Role has_many :memberships. I''ll admit, though, that in this case I don''t really see that the Membership model is any use. Why aren''t you just connecting User and Role directly? Best, -- Marnen Laibow-Koser marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org http://www.marnen.org -- Posted via http://www.ruby-forum.com/.
Brian wrote:> Apparently I assumed functionality that doesn''t exist for this > association. Should I not be able to reference the target model as an > attribute? > > Here are my models: > > class User < ActiveRecord::Base > has_one :membership > has_one :role, :through => :membership > end > > class Membership < ActiveRecord::Base > belongs_to :user > belongs_to :role > end > > class Role < ActiveRecord::Base > end > >Hi Brian, I think even if it is has_one: through or has_many :through, you should define - class User < ActiveRecord::Base has_many :membership has_one :role, :through => :membership end when defining user and membership. Then try user.role I have worked on has_many :through recently. My example - http://manasivora.blogspot.com Let me know if it helps - Thanks, Manasi -- Posted via http://www.ruby-forum.com/.
On May 20, 8:02 am, Manasi Vora <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Hi Brian, > I think even if it is has_one: through or has_many :through, you should > define - > class User < ActiveRecord::Base > has_many :membership > has_one :role, :through => :membership > end > > when defining user and membership. Then try user.roleThanks for the tip, Manasi. This didn''t solve the problem. After defining them as you suggest I do the following: user = User.new user.save! membership = user.memberships.create role = membership.create_role And then in the debugger: (rdb:1) user #<User id: 1, created_at: "2009-05-20 17:34:08", updated_at: "2009-05-20 17:34:0 8"> (rdb:1) user.memberships.find(:first) #<Membership id: 996332878, user_id: 1, role_id: nil, created_at: "2009-05-20 17 :34:08", updated_at: "2009-05-20 17:34:08"> (rdb:1) membership #<Membership id: 996332878, user_id: 1, role_id: 1, created_at: "2009-05-20 17:3 4:08", updated_at: "2009-05-20 17:34:08"> (rdb:1) membership.role #<Role id: 1, created_at: "2009-05-20 17:34:08", updated_at: "2009-05-20 17:34:0 8"> (rdb:1) user.role nil Now the interesting thing is that user.memberships.find(:first) has no role_id, but membership does. I''m not sure what this means, but I''ll explore it further. I suspect my problem might be cached records, but: (rdb:1) user.memberships(true).find(:first) #<Membership id: 996332878, user_id: 1, role_id: nil, created_at: "2009-05-20 17 :34:08", updated_at: "2009-05-20 17:34:08"> I thought the ''true'' caused this to be reloaded, so I don''t have an explanation for the discrepency. I''m a little relieved this didn''t work, since the combination of has_many memberships and has_one role doesn''t make sense to me.
On May 20, 2:02 am, Marnen Laibow-Koser <rails-mailing-l...@andreas- s.net> wrote:> I don''t know if this is the problem, but I think you forgot Role > has_many :memberships. >I don''t think methods on the Role class should affect this in anyway. I should only define the association if I benefit from the methods it provides, right? Or are there other effects I should be aware of? Anyways, I gave this a try and the results are the same.> I''ll admit, though, that in this case I don''t really see that the > Membership model is any use. Why aren''t you just connecting User and > Role directly?Fair question. You''re probably right that this should be redesigned. The reasons I ended up with this are: 1) Originally I had planned to have a has_many relationship between user and role. When my plan changed I was lazy and didn''t change models/schema. 2) Personal taste. I don''t like to have empty fields in my database by design. If a record exists, all of the fields should have a value. This design lets a user have 0 or 1 roles and I test for this by looking for the existance of a record in the join table. I don''t know why this bothers me and it''s probably irrational. :) 3) The example is somewhat contrived, though it does mimic an application I''m writing. I left out all of the fields for these tables except the IDs so that I could demostrate the problem here.
Now I''m especially stumped. I rewrote this as a has_many :through: class User < ActiveRecord::Base has_many :memberships has_many :roles, :through => :memberships end class Membership < ActiveRecord::Base belongs_to :user belongs_to :role end class Role < ActiveRecord::Base has_many :memberships end But when I create a user, membership, and role, I still cannot access role through user: user = User.new user.save! membership = user.memberships.create role = membership.create_role When I look at the value of user.roles(true) I get an empty array. Obviously I''m doing something very wrong.
This may be a dumb question, but what version of Rails are you on? I know that versions earlier than 2.2.2 had some difficulties with has_one :through... On May 20, 1:47 am, Brian <butler.bria...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Apparently I assumed functionality that doesn''t exist for this > association. Should I not be able to reference the target model as an > attribute? > > Here are my models: > > class User < ActiveRecord::Base > has_one :membership > has_one :role, :through => :membership > end > > class Membership < ActiveRecord::Base > belongs_to :user > belongs_to :role > end > > class Role < ActiveRecord::Base > end > > But, the "role" attribute for an instance of User always contains nil: > > >> user = User.new > > => #<User id: nil, created_at: nil, updated_at: nil>>> user.save! > => true > >> membership = user.create_membership > > => #<Membership id: 1, user_id: 1, role_id: nil, created_at: > "2009-05-20 05:40:1 > 7", updated_at: "2009-05-20 05:40:17">>> role = membership.create_role > > => #<Role id: 1, created_at: "2009-05-20 05:40:33", updated_at: > "2009-05-20 05:4 > 0:33">>> user.membership > > => #<Membership id: 1, user_id: 1, role_id: 1, created_at: "2009-05-20 > 05:40:17" > , updated_at: "2009-05-20 05:40:17">>> user.membership.role > > => #<Role id: 1, created_at: "2009-05-20 05:40:33", updated_at: > "2009-05-20 05:4 > 0:33"> > > >> user.role > => nil > >> quit > > Why does user.membership.role contain a record, but user.role does not?
On May 20, 10:17 pm, Matt Jones <al2o...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> This may be a dumb question, but what version of Rails are you on? I > know that versions earlier than 2.2.2 had some difficulties with > has_one :through... >Yeah, I was looking at some related bug fixes, but I think they are all incorporated. I''m using 2.3.2. There are a couple things I''ll try later tonight when I have a chance to look again: First, the debugger is giving me something strange in the AssociationProxy class in the reload method. After calling reset (it uses AssociationCollection#reset) @loaded == true according to the debugger. If loaded really is true, it might expliain this, but I''m still under the assumption that I''m misinterpreting the debugger, or the debugger is wrong, because otherwise I misunderstand inheritance in ruby and there''s a bug in rails (this seems less likely). I haven''t been able to duplicate this with a smaller repro, but I''ll try again tonight. Second, I think it''s very likely that my scenario isn''t all that common. And it''s very likely that the association implementation wasn''t designed with this in mind. I''m running into this in a unit test. Instead of creating models in memory, I''ll load up data from the test DB, and I''m guessing user.roles suddenly starts working. Then at least I know I can work around the problem with test fixtures. In the meantime, if anyone else has an idea of what''s going on, let me know.
To follow-up, this works fine with test fixtures. It also works fine if I do things in a slightly different order: user=User.new user.build_membership user.membership.build_role user.save! When I do this, user.role no longer returns nil, it returns the role as expected. I think things weren''t cached quite right, and for whatever reason I couldn''t force a proper reload. Maybe a bug in rails?
Hi Brian.. Glad that you could figure out the solution !! .. In my earlier reply what I meant was that for defining has_many :through, you need to declare - has_many :memberships has_many :roles, :through => memberships Please note that has_many :Pluralised model needs to be established. Then you may define has_many through or has_one through.. Also when you say debugger, are you referring to ruby console ? I work a lot on the console and have never come across any caching issue. Try it if you would like - ruby script/console Cheers, Manasi -- Posted via http://www.ruby-forum.com/.
Yep, working with AssociationProxy stuff in either the debugger or the console is nasty. Things have a tendency to happen without being told to (especially loading the target). Also, have you tried calling membership.save in your original example? The code you show will set the FK in membership (via membership.create_role), but it''s not on the DB until you save it... --Matt Jones On May 20, 10:34 pm, Brian <butler.bria...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > First, the debugger is giving me something strange in the > AssociationProxy class in the reload method. After calling reset (it > uses AssociationCollection#reset) @loaded == true according to the > debugger. If loaded really is true, it might expliain this, but I''m > still under the assumption that I''m misinterpreting the debugger, or > the debugger is wrong, because otherwise I misunderstand inheritance > in ruby and there''s a bug in rails (this seems less likely). I > haven''t been able to duplicate this with a smaller repro, but I''ll try > again tonight. > > Second, I think it''s very likely that my scenario isn''t all that > common. And it''s very likely that the association implementation > wasn''t designed with this in mind. I''m running into this in a unit > test. Instead of creating models in memory, I''ll load up data from > the test DB, and I''m guessing user.roles suddenly starts working. > Then at least I know I can work around the problem with test fixtures. > > In the meantime, if anyone else has an idea of what''s going on, let me > know.
Yes, I figured out what you meant in your original mail. Even when I used has_many I wasn''t able to get anything useful from user.roles. By debugger I mean the ruby-debug gem, though I''ve also used the console for a lot of this too. On May 21, 2:20 pm, Manasi Vora <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Hi Brian.. > Glad that you could figure out the solution !! .. In my earlier reply > what I meant was that for defining has_many :through, you need to > declare - > has_many :memberships > has_many :roles, :through => memberships > > Please note that has_many :Pluralised model needs to be established. > Then you may define has_many through or has_one through.. > > Also when you say debugger, are you referring to ruby console ? I work a > lot on the console and have never come across any caching issue. Try it > if you would like - > ruby script/console > > Cheers, > Manasi > -- > Posted viahttp://www.ruby-forum.com/.
On May 21, 3:38 pm, Matt Jones <al2o...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Also, have you tried calling membership.save in your original example? > The code you show will set the FK in membership (via > membership.create_role), but it''s not on the DB until you save it... > > --Matt Jones >This lead me to believe otherwise: http://guides.rubyonrails.org/association_basics.html#has-one-association-reference Specifically, "The create_association method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set. In addition, the associated object will be saved (assuming that it passes any validations)." Is the guide wrong about this being saved? In any case, I actually did try this with the build_association followed by a .save! and got exactly the same results.
What you''re describing would look like: membership.build_role.save! What I''m talking about is: membership.create_role membership.save --Matt Jones On May 21, 5:07 pm, Brian <butler.bria...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> On May 21, 3:38 pm, Matt Jones <al2o...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > > Also, have you tried calling membership.save in your original example? > > The code you show will set the FK in membership (via > > membership.create_role), but it''s not on the DB until you save it... > > > --Matt Jones > > This lead me to believe otherwise:http://guides.rubyonrails.org/association_basics.html#has-one-associa... > > Specifically, "The create_association method returns a new object of > the associated type. This object will be instantiated from the passed > attributes, and the link through its foreign key will be set. In > addition, the associated object will be saved (assuming that it passes > any validations)." > > Is the guide wrong about this being saved? In any case, I actually > did try this with the build_association followed by a .save! and got > exactly the same results.