Hello all, I was hoping someone could explain to me why both sides of a has_many :through association need to not be new_records? Wouldn''t the save be able to succeed if the object with the has_many :through association was the only new_record? It seems to me that because something like p = posts.new p.taggings << Tagging.new(:tag_id => 1) p.save works, that something like p = posts.new p.tags << Tags.create p.save should be able to work since rails should be able to do essentially what happens in the first example under the covers. Maybe it''s difficult to manage what p.tags returns before you''ve saved it, or something like that? Is it the sort of thing that could be done, it''s just high effort, or is there some impossible case I''m missing? After I started thinking about it I became very curious, so I googled around, searched trac, but didn''t find anything. I''d be very appreciative for any enlightenment on the subject. Thanks, Jason --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
On Dec 8, 2007, at 10:10 PM, Jason wrote:> I was hoping someone could explain to me why both sides of a has_many > :through association need to not be new_records? Wouldn''t the save be > able to succeed if the object with the has_many :through association > was the only new_record? > > It seems to me that because something like > > p = posts.new > p.taggings << Tagging.new(:tag_id => 1) > p.save > > works, that something like > > p = posts.new > p.tags << Tags.create > p.save > > should be able to work since rails should be able to do essentially > what happens in the first example under the covers. > > Maybe it''s difficult to manage what p.tags returns before you''ve saved > it, or something like that? Is it the sort of thing that could be > done, it''s just high effort, or is there some impossible case I''m > missing? > > After I started thinking about it I became very curious, so I googled > around, searched trac, but didn''t find anything. I''d be very > appreciative for any enlightenment on the subject.The new_record association stuff in ActiveRecord is pretty hairy, and doesn''t always work as expected. It''s a poor man''s Unit-of-Work[1] but only for special circumstances. The new_record hack only works when the containing model (the one with the has_many) and the content models (the ones with belongs_to) are both new. Instead of setting the fk (e.g. the container_id field) in the content object, the association waits until the container is saved and gets a primary key. Then it saves all the content objects with that new container pk as the content fk. The intractable problem with has_many :through is that you would have to order a bunch of saves to get everything to happen correctly. For your example, you''d have to save all the tag and post records first, then you could save the tagging records. But there is no UnitOfWork to help you out, that is, no global knowledge of all the new tags and posts that need saving. You could try starting with one post and saving it, but then you''d have to go through its taggings to save their tags too, since you need both tag and post saved before you can set the fks in the tagging and save it. Since each tag may have many taggings, you can get into an ordering mess that is impossible (or maybe just very difficult) to linearize, at least given the constraints of how ActiveRecord works. [1] http://martinfowler.com/eaaCatalog/unitOfWork.html -- Josh Susser http://blog.hasmanythrough.com --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
Thank you very much for responding. Unfortunately, I''m still confused. I''m not sure exactly what you''re talking about when you say "he new_record hack". Are you referring to the code that allows me to save a ''new'' tag and many ''new'' taggings all in one go? I''m still not sure what the difficulty is though in the specific case where all the Tags already exist in the DB. My usage of Tag.create (Tags was a typo) in my example was intended to signify that the Tag would already exist in the DB so the Taggings that rails would create when saving the Post would be able to use the Tag''s id. If the Tag already exists then shouldn''t the tagging that rails makes up to associate with the Post be able to use it''s id, thereby making it essentially the same as if I this? post.taggings << Tagging.new(:tag_id => n) I guess a related question, or maybe it''s the same question in the end, is why the has_many_through_association (http://dev.rubyonrails.org/browser/trunk/activerecord/lib/active_record/associations/has_many_through_association.rb) ''<<'' method uses ''create!'' instead of ''new''? Thanks, Jason On Dec 9, 2007 2:47 AM, Josh Susser <josh@hasmanythrough.com> wrote:> > > > On Dec 8, 2007, at 10:10 PM, Jason wrote: > > I was hoping someone could explain to me why both sides of a has_many > > :through association need to not be new_records? Wouldn''t the save be > > able to succeed if the object with the has_many :through association > > was the only new_record? > > > > It seems to me that because something like > > > > p = posts.new > > p.taggings << Tagging.new(:tag_id => 1) > > p.save > > > > works, that something like > > > > p = posts.new > > p.tags << Tags.create > > p.save > > > > should be able to work since rails should be able to do essentially > > what happens in the first example under the covers. > > > > Maybe it''s difficult to manage what p.tags returns before you''ve saved > > it, or something like that? Is it the sort of thing that could be > > done, it''s just high effort, or is there some impossible case I''m > > missing? > > > > After I started thinking about it I became very curious, so I googled > > around, searched trac, but didn''t find anything. I''d be very > > appreciative for any enlightenment on the subject. > > > The new_record association stuff in ActiveRecord is pretty hairy, and > doesn''t always work as expected. It''s a poor man''s Unit-of-Work[1] > but only for special circumstances. The new_record hack only works > when the containing model (the one with the has_many) and the content > models (the ones with belongs_to) are both new. Instead of setting > the fk (e.g. the container_id field) in the content object, the > association waits until the container is saved and gets a primary > key. Then it saves all the content objects with that new container pk > as the content fk. > > The intractable problem with has_many :through is that you would have > to order a bunch of saves to get everything to happen correctly. For > your example, you''d have to save all the tag and post records first, > then you could save the tagging records. But there is no UnitOfWork > to help you out, that is, no global knowledge of all the new tags and > posts that need saving. You could try starting with one post and > saving it, but then you''d have to go through its taggings to save > their tags too, since you need both tag and post saved before you can > set the fks in the tagging and save it. Since each tag may have many > taggings, you can get into an ordering mess that is impossible (or > maybe just very difficult) to linearize, at least given the > constraints of how ActiveRecord works. > > [1] http://martinfowler.com/eaaCatalog/unitOfWork.html > > -- > Josh Susser > http://blog.hasmanythrough.com > > > > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
First of all, you cannot call << on when parent is a new record. p = posts.new p.taggings << Tagging.new(:tag_id => 1) p.save That''d raise an exception. On Dec 9, 2007 8:31 AM, Jason <random.numbers@gmail.com> wrote:> > I guess a related question, or maybe it''s the same question in the > end, is why the has_many_through_association > (http://dev.rubyonrails.org/browser/trunk/activerecord/lib/active_record/associations/has_many_through_association.rb) > ''<<'' method uses ''create!'' instead of ''new''? >Well, that''d be a design choice. You can always use post.taggings.new or post.taggings.build -- Cheers! - Pratik http://m.onkey.org --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
I know it raises an exception the way it''s coded right now, my question is why? Why was the design choice made to work this way, instead of allowing "post.tags << Tag.create" to work, since the tag would have an id? I think it would be mighty handy if it worked, so I''d certainly consider trying to create a patch, I just wanted to know if there''s some solid rationale behind the current method that I''m missing. Thanks, Jason On Dec 9, 2007 6:45 AM, Pratik <pratiknaik@gmail.com> wrote:> > First of all, you cannot call << on when parent is a new record. > > p = posts.new > p.taggings << Tagging.new(:tag_id => 1) > p.save > > That''d raise an exception. > > On Dec 9, 2007 8:31 AM, Jason <random.numbers@gmail.com> wrote: > > > > I guess a related question, or maybe it''s the same question in the > > end, is why the has_many_through_association > > (http://dev.rubyonrails.org/browser/trunk/activerecord/lib/active_record/associations/has_many_through_association.rb) > > ''<<'' method uses ''create!'' instead of ''new''? > > > > Well, that''d be a design choice. You can always use post.taggings.new > or post.taggings.build > > -- > Cheers! > - Pratik > http://m.onkey.org > > > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
On Dec 9, 2007 11:13 PM, Jason <random.numbers@gmail.com> wrote:> > I know it raises an exception the way it''s coded right now, my > question is why? Why was the design choice made to work this way, > instead of allowing "post.tags << Tag.create" to work, since the tag > would have an id?What''s the solid rationale behind trying to create tags for unsaved post ? Why can''t you just do post.save first and then do post.tags << Tag.create ? The current behavior is to make sure you don''t end up having dangling children. And believe me, many people found some defect in their apps when the current behavior was introduced. -- Cheers! - Pratik http://m.onkey.org --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
Well, mapping my particular situation onto Tags and Posts, I don''t care at all if I have a Tag that doesn''t have any posts, but I insist that Posts be tagged. So right now I''m forced to save the Post without its Tag validation, then add the Tag (or Tagging as it were) and save again. It''s a real hassle if happens that makes the Post invalid on the second save, then I have to clean up from the first save. It seems like that''s more work, and less readable than if "post.tags << Tag.create" worked. I''d like to be able to display a form for a Post, and show its Tags, without having to touch the DB, and without having to jump through association hoops. Mostly I''m just curious if anyone can tell me why it works the way it does right now since at the very least it means writing a few more lines of code. Thanks, Jason On Dec 9, 2007 6:50 PM, Pratik <pratiknaik@gmail.com> wrote:> > On Dec 9, 2007 11:13 PM, Jason <random.numbers@gmail.com> wrote: > > > > I know it raises an exception the way it''s coded right now, my > > question is why? Why was the design choice made to work this way, > > instead of allowing "post.tags << Tag.create" to work, since the tag > > would have an id? > > What''s the solid rationale behind trying to create tags for unsaved > post ? Why can''t you just do post.save first and then do post.tags << > Tag.create ? The current behavior is to make sure you don''t end up > having dangling children. > > And believe me, many people found some defect in their apps when the > current behavior was introduced. > -- > > Cheers! > - Pratik > http://m.onkey.org > > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
On Dec 9, 7:22 pm, Jason <random.numb...@gmail.com> wrote:> It''s a real hassle if happens that makes the Post > invalid on the second save, then I have to clean up from the first > save.Can you just wrap everything in a transaction? The you don''t have to do any manual cleanup. But I agree, it feels funny to have to save the Post in a seemingly invalid state up front. Jeff softiesonrails.com --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
Yeah, as it is right now I''m doing it with transactions, and some extra validation, but it''d be great if I could get rid of the extra code. On Dec 9, 2007 9:32 PM, Jeff <cohen.jeff@gmail.com> wrote:> > On Dec 9, 7:22 pm, Jason <random.numb...@gmail.com> wrote: > > It''s a real hassle if happens that makes the Post > > invalid on the second save, then I have to clean up from the first > > save. > > Can you just wrap everything in a transaction? The you don''t have to > do any manual cleanup. > > But I agree, it feels funny to have to save the Post in a seemingly > invalid state up front. > > Jeff > softiesonrails.com > > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---