Preload relationship during action

I know we can use load to load an association during an action. However this only loads the relationship after the action is completed. I would like to do something like this:
action :update do
change set_attribute(:starts_at, DateTime.utc_now(), new?: true)
change load([:my_resource])
change SomeChangeThatUsesResource
end
action :update do
change set_attribute(:starts_at, DateTime.utc_now(), new?: true)
change load([:my_resource])
change SomeChangeThatUsesResource
end
Using :my_resource in the next change doesn't preload the relationship at the moment to I get a #Ash.NotLoaded<:relationship> . Im sure this is possible? Wasn't able to figure it out from the docs so wondering if anyone has a similar situation
16 Replies
ZachDaniel
ZachDaniel2y ago
Youd need a custom change to accomplish this
gordoneliel
gordonelielOP2y ago
So the change will just do a fetch? I guess im curious why the load happens after and not inline? Im curious how the resource gets loaded when it hits an action? For my use case its from the AshJsonApi. Is it possible to preload at that level? Also my changes after are in an "after action", will it be preloaded by the time im in the after action?
ZachDaniel
ZachDaniel2y ago
The load happens after since its meant to have the data loaded "after" the action for any thing calling it but your change could do something accidentally (or on purpose) that would scrub that value like if you take the loaded record and do something and return it via reloading or something, oops you've accidentally removed the thing that was meant to be loaded You can programatically use, so if you have a change that wants to load some data during an action, it kinda depends when and where you need it you can load things programatically if you have the record with things like Api.load(record, :relationship)
gordoneliel
gordonelielOP2y ago
Hm, it still seems a bit weird since the after actions are supposed to be after the action? so the load should be before this I was assuming. I tried Api.load, which works in the after action, but its doesnt do any optimization for already loaded relationships it seems. Is it possible to preload in the changeset level before an after action?
ZachDaniel
ZachDaniel2y ago
The after actions aren't really after the "entire action". Its probably a bad name, now that you say it Its more like after_data_layer_action You can do lazy?: true as an option to your load to not overwrite already loaded stuff
gordoneliel
gordonelielOP2y ago
Ah that makes sense. I guess what im trying to figure out is whats the recommended way to preload stuff to use down the action + changes pipeline. Is it better to have a special change that preloads this? or do you just do it inline where neede?
ZachDaniel
ZachDaniel2y ago
I typically don't load data in the actions at all TBH I make the caller ask for what additional data they need although, I see what you're saying you have hooks that need the data In that case yeah I usually do it in-line where I need it or in a custom change. The story there could definitely be better
gordoneliel
gordonelielOP2y ago
yeah what would be nice would be something similar to load, say preload([:relationships]) and would load the relationships if not already loaded. For context, I typically publish integration events after actions using Oban as an outbox, and serialize the current state of the resource + some relationships into an Oban job to be sent over a message bus, so I run into this sorta stuff all the time
ZachDaniel
ZachDaniel2y ago
Yeah, the main difficult with it is actually the ordering of events
change preload()
change something_after_action()
change preload()
change something_after_action()
If both of those add an after_action hook, they go in reverse order like the last-added after_action hook goes first, and so-on its a stupid problem and honestly....I might change that for 3.0 @jart thoughts? should we make after_action and before_action hooks go in the order they are added for 3.0?
jart
jart2y ago
I think we probably should just because the current behaviour violates the principle of least surprise.
ZachDaniel
ZachDaniel2y ago
Yeah, because we're mostly composing actions
jart
jart2y ago
But also I wonder if there’s not a better way of expressing it to make the dependencies clear to the machine and the user.
ZachDaniel
ZachDaniel2y ago
and
change after_action(foo)
change after_action(bar)
change after_action(foo)
change after_action(bar)
does not at all look like bar goes before foo. The real difficulty is that sometimes you want to add after_action hooks in the core changes block that go after everything There are some answers. We should revisit for 3.0. I think for complex workflows it makes sense to use something else, but we also want to enable people not to have to reach for that for relatively simple actions i.e load some data, do a side effect, return the result
jart
jart2y ago
Yup. I was thinking something like
ordered_before_actions [Change1, Change2]
ordered_before_actions [Change1, Change2]
Or something like that that makes the order explicit.
ZachDaniel
ZachDaniel2y ago
Hmm.... Yeah, its an interesting idea Maybe we should store alongside the function the config of prepend? and append? and then if something was made with prepend? and something else is added without prepend? then it goes after all the prepend? hooks Essentially introducing three more meta-phases hah not that people will really need to think about it that way it just makes prepend? and append? do what you think more often. But it might just be way too confusing. Okay, I think I see what we should do. In 3.0: hooks happen in the order that they are added. Both before_action and after_action. the global changes block only, supports before_action_changes?: true | false (defaulting to true) If its within a single action, its easy enough to move them around. Its only the interaction between before/after action. And if the added hooks will be in the proper order, then also the order of the changes will be correct. I think this is the answer that makes things work the way you expect, but lets you choose where global changes happen
ZachDaniel
ZachDaniel2y ago
GitHub
Better action hook order in 3.0 · Issue #724 · ash-project/ash
Right now hook execution order does not follow the principle of least surprise. They happen essentially in reverse order from how they were added. This makes sense in a functional hook builder but ...

Did you find this page helpful?