On Fri, Nov 19, 2021 at 7:26 PM [email protected] <
[email protected]> 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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/sequel-talk/CADGZSSfY8may50tUEVZhG_8PoENLxQg3Oq18DUst0%3D3J9g-7DA%40mail.gmail.com.