Thanks for your quick response and fix! I think adding the default in after_save should work just fine.
Happy to hear that the example helped fix the bug. Sequel has been a pleasure to work with so I'm glad to be able to contribute in any way I can! On Saturday, November 20, 2021 at 12:04:10 AM UTC-8 Jeremy Evans wrote: > On Fri, Nov 19, 2021 at 7:26 PM s.brimd...@gmail.com <s.brimd...@gmail.com> > wrote: > >> I'm having some trouble using nested_attributes in conjunction with the >> tactical_eager_loading plugin. >> >> Specifically, I'm trying to set a default value for a one_to_one >> association in before_validation only if that association doesn't already >> have an associated object via an update of a one_to_many association also >> using nested attributes. However, it seems that the association is always >> cleared by the time we get to before_validation so we always end up with >> the default. >> >> Here is a basic setup to illustrate the problem: >> >> DB.create_table :albums do >> primary_key :id >> column :title, :text >> end >> >> DB.create_table :tracks do >> primary_key :id >> column :title, :text >> foreign_key :album_id, :albums, null: false >> end >> >> DB.create_table :studios do >> primary_key :id >> column :name, :text >> foreign_key :track_id, :tracks >> end >> >> Sequel::Model.plugin :nested_attributes >> Sequel::Model.plugin :tactical_eager_loading >> >> class Album < Sequel::Model >> one_to_many :tracks >> nested_attributes :tracks >> end >> >> class Track < Sequel::Model >> many_to_one :album >> one_to_one :studio >> nested_attributes :studio >> >> def before_validation >> self.studio_attributes = { name: "Default Studio" } if !studio >> >> super >> end >> end >> >> class Studio < Sequel::Model >> one_to_one :track >> end >> >> album = Album.create \ >> title: "Title", >> tracks_attributes: [{ >> title: "First track", >> studio_attributes: { >> name: "MY favorite studio" >> } >> }] >> >> album = Album.first >> album.update \ >> tracks_attributes: [{ >> id: album.tracks.first.id, >> title: "Renamed" >> }, { >> title: "A New track", >> studio_attributes: { >> name: "My second favorite studio" >> } >> }] >> >> expected = "My second favorite studio" >> actual = Track[title: "A New track"].studio.name >> >> raise "expected: #{expected}, actual: #{actual}" if expected != actual >> >> >> For the new track being added in the update, the studio= setter is being >> called twice because the studio association is nil in >> Track#before_validation. We tracked it down to the >> initialize_association_cache clearing the association via >> tactical_eager_loading after studio_attributes= is first called. >> >> Is there a more conventional way to do this you could suggest or is it >> indeed a bug? >> > > First, thanks for providing a self-contained reproducible example. That > makes things much easier. > > In terms of working around the issue, you could switch the > before_validation to after_save, and don't use studio_attributes inside it: > > def after_save > super > self.studio ||= Studio.new(name: "Default Studio") > end > > That should work fine if you don't need to do some special validation of > the studio when saving the track. You don't need a presence validation or > similar, since if there isn't a studio set when validating, you know one > will be set after save. > > In terms of why your code doesn't work, it looks like it's due to a bug in > tactical eager loading. Tactical eager loading will try to eager load for > all objects if the current object doesn't have a cached association. It > should probably only eager load for objects that don't have a cached > association if the current object doesn't have a cached association. I > committed a fix for this: > https://github.com/jeremyevans/sequel/commit/288ae6f210842cbca54d3a18425117ad1d782ff4 > > Your example was very helpful, it helped fix this bug, which has been > present in tactical_eager_loading since it was originally introduced over > 12 years ago. > > Thanks, > Jeremy > -- You received this message because you are subscribed to the Google Groups "sequel-talk" group. To unsubscribe from this group and stop receiving emails from it, send an email to sequel-talk+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/sequel-talk/c98e8910-da11-4ad5-919e-4164a62ffa35n%40googlegroups.com.