Joe MooreJoe Moore
Standup 11/18/2008: Unbelievable has_many :through Gotcha
edit Posted by Joe Moore on Tuesday November 18, 2008 at 11:53PM
  • One team discovered a jaw-dropping issue with has_many :through. Given the following:

    class User < ActiveRecord::Base
      has_many :user_photos
      has_many :photos, :through => :user_photos
    
    • a_user.photos.create will create and persist both a Photo object and the UserPhoto join object
    • photo = a_user.photos.build followed by photo.save will create and persist the Photo object only, and will not persist an appropriate UserPhoto join object.
  • Rails 2.2: Test::Unit::TestCase extentions have been removed from Rails Core and are now in ActiveSupport::TestCase. As stated in the Groups Thread about this, use ActiveSupport::TestCase instead of Test::Unit::TestCase in test/test_helper.rb.

Comments

  1. Mark Wilden Mark Wilden on November 19, 2008 at 01:04AM

    I don't see how photo.save could know anything about UserPhoto.

    That said, some of my most initially frustrating moments with Rails came when trying to create associations: e.g., adding a has_many object when the owner hasn't been saved yet.

  2. stefano stefano on November 19, 2008 at 08:44AM

    I may be wrong, but I don't think you're supposed to create objects via a has_many_through association. For me, it's just a convenient way to express a join. I remember that when we defined "flattened relationships" in WebObjects (same concept) they were invariably read-only.

  3. Josh Susser Josh Susser on November 19, 2008 at 01:23PM

    Stefano, you can indeed create join model object via a has_many :through association. But there are limitations.

    One of the quirks of hmt is that you need to have both endpoint objects saved to be able to create the join model object, so that it has both foreign keys / ids to work with. The hmt #create method can create the join model since it's creating the endpoint object. But #build explicitly doesn't save the endpoint, meaning no id, so there's no way to create the join model. Yes, this seems broken, or at least confusing. There's probably a way to fix it, but it would require some thought about what the right thing to do is.

Add a Comment (MarkDown available)