Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

has_many :through should work if the join model uses has_one relationship

#2719

Let's say you have User has_many Posts, and each Post has_one Photo. If you try to set a has_many :photos, :through :posts and then access user.photos you will get something like this:

ActiveRecord::HasManyThroughSourceAssociationMacroError: Invalid source reflection macro :has_one for has_many :photos, :through => :posts.  Use :source to specify the source reflection.

Besides the fact that the error message isn't too helpful, it appears that this isn't supported in Rails 2.3.2 or previously. It would be awfully nice to be able to, for example, list all of the Photos for a certain User, etc.

There is an old bug in Rails Trac for this, which can no longer be modified.

It's not an uncommon problem: http://railsforum.com/viewtopic.php?pid=86321#p86321 and http://railsforum.com/viewtopic.php?id=2295 and http://railsforum.com/viewtopic.php?id=265#p4120 and http://railsforum.com/viewtopic.php?pid=6388#p6388

Is there a suggested workaround? (Aside from hacking SQL...)

Reported by sbwoodside · May 26th, 2009 @ 05:07 AM

State: resolved
Milestone: none
Assigned to: nobody
Importance: none

Activity

  1. CancelProfileIsBroken
    CancelProfileIsBroken
    • State changed from new to incomplete

    This looks like a duplicate of #1149 Invalid has_one :through source_reflection macros. Bottom line: hasn't ever worked, documented as not working on http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassM.... If you'd like to submit a patch and failing test case, I'm sure core team would consider.

    Potential workaround: change to Post has_many :photos, though obviously that changes the semantics and you'd have to enforce uniqueness in other code.

    May 26th, 2009 @ 01:36 PM

  2. sbwoodside
    sbwoodside

    Is it not permissible to have a bug open on this as something to fix at some point? The workaround is not something I would like to do.

    June 8th, 2009 @ 03:21 AM

  3. sbwoodside
    sbwoodside

    Well, this seems to work as a hackish work-around and relatively efficient if most of your posts have a photo. I'm not really an SQL guru so if someone could post better SQL that would be nice :-)

    @photos = @user.posts.map { |post| post.photo } .compact

    June 8th, 2009 @ 03:41 AM

  4. Josh Sharpe
    Josh Sharpe

    This patch seems to work for me, but I really don't know enough about the inner workings of associations to be sure.

    July 11th, 2009 @ 11:22 PM

  5. CancelProfileIsBroken
    CancelProfileIsBroken
    • Tag changed from activerecord, association, associations, hasmanythroughsourceassociationmacroerror, has_many, has_one, through to activerecord, association, associations, bugmash, hasmanythroughsourceassociationmacroerror, has_many, has_one, through
    • State changed from incomplete to open

    August 4th, 2009 @ 08:15 PM

  6. Gabe da Silveira
    Gabe da Silveira

    verified that the patch applies to 2-3-stable and tests pass for mysql/sqlite/postgres. Going to look a little deeper now for potential problems...

    August 9th, 2009 @ 12:45 AM

  7. Gabe da Silveira
    Gabe da Silveira

    After looking at this deeper the main problem is with inserting and deleting records via the has_many :through => has_one relationship. However the main use case of this association is read-only anyway, so I think there is good value here, and I believe Josh's patch works smoothly because of support for has_many :through has_many.

    I believe the proper thing to do here is add an exception similar to HasManyThroughCantAssociateThroughHasManyReflection to make the read-only functionality explicit and update the documentation. I'm willing to do that now if there is agreement that this is the right approach.

    August 9th, 2009 @ 03:47 AM

  8. Gabe da Silveira
    Gabe da Silveira
    • Tag changed from activerecord, association, associations, bugmash, hasmanythroughsourceassociationmacroerror, has_many, has_one, through to activerecord, association, associations, bugmash, hasmanythroughsourceassociationmacroerror, has_many, has_one, through, verified

    Okay, I've attached a patch. This enabled has_many :through for use with a has_one reflection on the join model. It's enabled as read-only with the same mechanics as has_many :through has_many. I started with Josh's patch and added the read-only test + documentation updates.

    August 9th, 2009 @ 05:03 AM

  9. Gabe da Silveira
    Gabe da Silveira

    Oops, I left an unused Exception in there. That is removed in this updated patch for 2-3-stable, and also I have a rebased version to apply to master.

    I've attached a patch.

    August 9th, 2009 @ 05:55 AM

  10. Gabe da Silveira
    Gabe da Silveira

    Per koz's request, here is an updated patch with an official deprection for the old exception for the 2-3 branch.

    August 9th, 2009 @ 11:29 AM

  11. Josh Nichols
    Josh Nichols

    +1 for the functionality. I could use this behavior one of my current projects.

    Unfortunately, I wasn't able to apply your last version of the patch against 2-3-stable or master.

    August 9th, 2009 @ 08:31 PM

  12. Tristan Dunn
    Tristan Dunn

    +1 Verified

    Cleanly applies (apart from whitespace warning) and passes on master and 2-3-stable.

    August 9th, 2009 @ 08:33 PM

  13. Tristan Dunn
    Tristan Dunn

    Sorry, doesn't apply on the latest versions as Josh pointed out.

    August 9th, 2009 @ 08:36 PM

  14. Gabe da Silveira
    Gabe da Silveira

    Here are updated patches for 2-3-stable and master. Handy-dandy one-stop location:

    August 9th, 2009 @ 08:57 PM

  15. Gabe da Silveira
    Gabe da Silveira

    Crap. The deprecation snuck into master diff as well, which I don't think it should be according to my impression from koz. Without the deprecation:

    August 9th, 2009 @ 09:00 PM

  16. Josh Nichols
    Josh Nichols

    Verified that the patch applies to 2-3-stable and master, and that the functionality works as expected through manual testing.

    I ran these commands:

    rails 2719-demo
    cd 2719-demo
    script/generate  model user
    script/generate  model post user_id:integer
    script/generate  model photo post_id:integer
    rake db:create db:migrate
    

    Then used these models:

    class User < ActiveRecord::Base
      has_many :posts
      has_many :photos, :through => :posts
    end


    class Post < ActiveRecord::Base belongs_to :user has_one :photo end


    class Photo < ActiveRecord::Base belongs_to :post end

    And then played around in script/console:

    >> user = User.create!
    => #<User id: 2, created_at: "2009-08-09 20:51:27", updated_at: "2009-08-09 20:51:27">
    >> post = user.posts.create
    => #<Post id: 3, user_id: 2, created_at: "2009-08-09 20:51:34", updated_at: "2009-08-09 20:51:34">
    >> post.create_photo
    => #<Photo id: 3, post_id: 3, created_at: "2009-08-09 20:51:37", updated_at: "2009-08-09 20:51:37">
    >> user.photos
    => [#<Photo id: 3, post_id: 3, created_at: "2009-08-09 20:51:37", updated_at: "2009-08-09 20:51:37">]
    

    August 9th, 2009 @ 09:52 PM

  17. Repository
    Repository
    • State changed from open to resolved

    August 10th, 2009 @ 12:44 AM

  18. Repository
  19. CancelProfileIsBroken
    CancelProfileIsBroken
    • Tag changed from activerecord, association, associations, bugmash, hasmanythroughsourceassociationmacroerror, has_many, has_one, through, verified to activerecord, association, associations, hasmanythroughsourceassociationmacroerror, has_many, has_one, through, verified
    • Milestone cleared.

    August 10th, 2009 @ 02:15 AM